借助ThreadLocal实现多线程安全 | 带你学《Java语言高级特性》之三十二

上一篇:教你使用UUID、Optional类 | 带你学《Java语言高级特性》之三十一

本节结合消息发送案例简单直白地向读者展现了多线程模式下的线程安全问题,并向读者介绍了ThreadLocal类及其实现线程安全的原理,讲解了其使用方法。

【本节目标】
通过阅读本节内容,你将了解到多线程模式下重复性业务的安全问题和ThreadLocal能够实现线程安全的原理,学会使用ThreadLocal类来解决开发过程中的线程安全问题。

ThreadLocal类

在真正去了解ThreadLocal类作用的时候下面编写一个简单的程序做一个先期的分析。

范例:现在定义这样一个结构

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Message msg=new Message();   //实例化消息主体对象
        msg.setInfo("www.mldn.cn");     //设置要发送的内容
        Channel.setMessage(msg);    //设置要发送的消息
        Channel.send();    //发送消息
    }
}
class Channel {  //消息的发送通道
    private Channel(){}
    public static Message message;
    public static void setMessage(Message m) {
        message = m;
    }
    public static void send() {   //发送消息
        System.out.println("【消息发送】" + message.getInfo());
    }   //【消息发送】www.mldn.cn
}
class Message {    //要发送的消息体
    private String info;
    public void setInfo(String info) {
        this.info = info;
    }
    public String getInfo() {
        return info;
    }
}

借助ThreadLocal实现多线程安全 | 带你学《Java语言高级特性》之三十二
ThreadLocal类

对于当前的程序实际上采用的是一种单线程的模式来进行处理的;那么如果在多线程的状态下能否实现完全一致的操作效果呢?为此将启动三个线程进行处理。

范例:多线程的影响

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        new Thread(()->{
            Message msg=new Message();//实例化消息主体对象
            msg.setInfo("第一个线程的消息");//设置要发送的内容
            Channel.setMessage(msg);//设置要发送的消息
            Channel.send();//发送消息
        },"消息发送者A").start();
        new Thread(()->{
            Message msg=new Message();//实例化消息主体对象
            msg.setInfo("第二个线程的消息");//设置要发送的内容
            Channel.setMessage(msg);//设置要发送的消息
            Channel.send();//发送消息
        },"消息发送者B").start();
        new Thread(()->{
            Message msg=new Message();//实例化消息主体对象
            msg.setInfo("第三个线程的消息");//设置要发送的内容
            Channel.setMessage(msg);//设置要发送的消息
            Channel.send();//发送消息
        },"消息发送者C").start();
    }
}
class Channel {//消息的发送通道
    private Channel(){}
    public static Message message;
    public static void setMessage(Message m) {
        message = m;
    }
    public static void send() {//发送消息
        System.out.println("【"+Thread.currentThread().getName()+"、消息发送】" + message.getInfo());
    }
}
class Message {//要发送的消息体
    private String info;
    public void setInfo(String info) {
        this.info = info;
    }
    public String getInfo() {
        return info;
    }
}
/**
 * 【消息发送者A、消息发送】第二个线程的消息
 * 【消息发送者B、消息发送】第二个线程的消息
 * 【消息发送者C、消息发送】第三个线程的消息
 */

这个时候消息的处理产生了影响。

借助ThreadLocal实现多线程安全 | 带你学《Java语言高级特性》之三十二
考虑多线程ThreadLocal类

在保持Channel(所有发送的通道)核心结构不改变的情况下,需要考虑到每个线程的独立操作问题。那么在这样的情况下,发现对于Channel类而言,除了要保留有发送的消息之外,还应该多存放一个每一个线程的标记(当前线程),这个时候可以通过ThreadLocal类来存放数据。在ThreadLocal类里面提供有如下操作方法:

  • 构造方法:public ThreadLocal();
  • 设置数据:public void set(T value);
  • 取出数据:public T get();
  • 删除数据:public void remove();

借助ThreadLocal实现多线程安全 | 带你学《Java语言高级特性》之三十二
解决同步问题

范例:解决线程同步问题

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        new Thread(()->{
            Message msg=new Message();//实例化消息主体对象
            msg.setInfo("第一个线程的消息");//设置要发送的内容
            Channel.setMessage(msg);//设置要发送的消息
            Channel.send();//发送消息
        },"消息发送者A").start();
        new Thread(()->{
            Message msg=new Message();//实例化消息主体对象
            msg.setInfo("第二个线程的消息");//设置要发送的内容
            Channel.setMessage(msg);//设置要发送的消息
            Channel.send();//发送消息
        },"消息发送者B").start();
        new Thread(()->{
            Message msg=new Message();//实例化消息主体对象
            msg.setInfo("第三个线程的消息");//设置要发送的内容
            Channel.setMessage(msg);//设置要发送的消息
            Channel.send();//发送消息
        },"消息发送者C").start();
    }
}
class Channel {   //消息的发送通道
   private Channel(){}
   public static final ThreadLocal<Message> THREADLOCAL=new ThreadLocal<Message>();
   public static void setMessage(Message m) {
       THREADLOCAL.set(m);    //向ThreadLocal中保存数据
   }
   public static void send() {  //发送消息
       Message message=THREADLOCAL.get();
       System.out.println("【"+Thread.currentThread().getName()+"、消息发送】" + THREADLOCAL.get().getInfo());
   }
}
class Message {//要发送的消息体
    private String info;
    public void setInfo(String info) {
        this.info = info;
    }
    public String getInfo() {
        return info;
    }
}
/**
* 【消息发送者B、消息发送】第二个线程的消息
* 【消息发送者C、消息发送】第三个线程的消息
* 【消息发送者A、消息发送】第一个线程的消息
*/

每一个线程通过ThreadLocal只允许保存一个数据。

想学习更多的Java的课程吗?从小白到大神,从入门到精通,更多精彩不容错过!免费为您提供更多的学习资源。
本内容视频来源于阿里云大学

下一篇:手把手教你创建定时任务 | 带你学《Java语言高级特性》之三十三
更多Java面向对象编程文章查看此处

上一篇:静看Java多线程之路如何直通罗马 | 带你学《Java语言高级特性》之一


下一篇:另类的“同步”-volatile关键字 | 带你学《Java语言高级特性》之十四