ThreadLocal类基本使用

在多线程场景中,有一个常用的类ThreadLocal,该类作为一个桥梁可以将线程要操作的全局变量和线程本身绑定:

如下多线程测试程序:

package test.HimmaQ;

class Channel { //消息发送通道
    private  static Message message;
    private Channel(){};
    public static void setMessage(Message m){
        message = m;
    }
    public static void send(){//发送消息
        System.out.println(message.getInfo());
    }

}
class Message {
    private String info;
    public void setInfo(String info) {
        this.info = info;
    }
    public String getInfo(){
        return info;
    }
}

public class threadLocalTest {
    public static void main(String[] args) {
        new Thread(()->{
            Message msg = new Message();
            msg.setInfo("第一个线程发送消息");
            Channel.setMessage(msg);
            Channel.send();
        }).start();
        new Thread(()->{
            Message msg = new Message();
            msg.setInfo("第二个线程发送消息");
            Channel.setMessage(msg);
            Channel.send();
        }).start();
        new Thread(()->{
            Message msg = new Message();
            msg.setInfo("第三个线程发送消息");
            Channel.setMessage(msg);
            Channel.send();
        }).start();
    }
}

运行结果:

第三个线程发送消息
第三个线程发送消息
第三个线程发送消息

Process finished with exit code 0

可以看到,连续3个线程执行输出,输出结果与预期结果不符;

原因分析:对消息发送通道Channel类,其属性message是静态全局的,因此每一个线程过来都会操作该变量,当第一个线程为该对象设置了值还没有发出时,第二个线程又重新给它设置了值,同样的,第三个线程会覆盖前两个线程设置的值,因此在电脑速度快的情况下,我们只看到了第三个线程的输出结果。即,多线程对全局变量操作的情况下,发生了线程不安全(全局变量的值的覆盖)。

ThreadLoacl类

public class ThreadLocal<T>
extends Object

返回值 方法 说明
T get() 返回当前线程本地变量的当前线程的副本中的值。
void remove()
移除此线程局部变量的当前线程的值。
void set(T value) 将此线程局部变量的当前线程的副本设置为指定的值。
 

该类提供线程局部变量。通过其get和set方法,可以将线程要操作的全局变量的值与当前线程绑定。ThreadLocal实例通常是私有的静态字段,用在需要关联线程的时候,比如将用户id和当前线程绑定时候,可以将ID设置为ThreadLocal类。

可以将ThreadLocal类变量理解为一个map,其中存放的是每个线程与其对应的全局变量副本的键值对,在线程使用的时候,拿到的并不是全局变量本身,而是线程对应的全局变量的副本,这样就实现了线程和变量的绑定。

解决方案:

在Channel类中引入私有的全局ThreadLocal类变量,用来将线程和需要操作的message关联起来:

package test.HimmaQ;

class Channel { //消息发送通道
    private  static final ThreadLocal<Message> THREADLOCAL = new ThreadLocal<Message>();
    private Channel(){};
    public static void setMessage(Message m){
        THREADLOCAL.set(m);
    }
    public static void send(){//发送消息
        System.out.println(THREADLOCAL.get().getInfo());
    }

}
class Message {
    private String info;
    public void setInfo(String info) {
        this.info = info;
    }
    public String getInfo(){
        return info;
    }
}

public class threadLocalTest {
    public static void main(String[] args) {
        new Thread(()->{
            Message msg = new Message();
            msg.setInfo("第一个线程发送消息");
            Channel.setMessage(msg);
            Channel.send();
        }).start();
        new Thread(()->{
            Message msg = new Message();
            msg.setInfo("第二个线程发送消息");
            Channel.setMessage(msg);
            Channel.send();
        }).start();
        new Thread(()->{
            Message msg = new Message();
            msg.setInfo("第三个线程发送消息");
            Channel.setMessage(msg);
            Channel.send();
        }).start();
    }
}

运行结果:

第一个线程发送消息
第二个线程发送消息
第三个线程发送消息

Process finished with exit code 0

猜你喜欢

转载自www.cnblogs.com/HimmaQ/p/10229712.html