zoukankan      html  css  js  c++  java
  • JUC- ThreadLocal学习笔记

    ThreadLocal学习笔记

    说明:代码来自《阿里公开课》

    一、ThreadLocal是什么

    ThreadLocal 用作保存每个线程独享的对象,为每个线程都创建一个副本,这样每个线程都可以修改自己所拥有的副本, 而不会影响其他线程的副本,确保了线程安全。

    ThreadLocal 的使用:

    static final ThreadLocal<T> sThreadLocal = new ThreadLocal<T>();
    sThreadLocal.set()
    sThreadLocal.get()

    二、代码演示ThreadLocal的作用

    场景说明:

    1. 定义一个消息实体MessageEntity,用来作为消息传递的载体;

    2. 定义个发送消息的工具/通道Channel,用来发送消息;

    3. 定义一个main方法,创建多个线程,来验证测试;

    消息实体

    public class MessageEntity {
        private String message ;
    
        public MessageEntity(String message) {
            this.message = message;
        }
    
        public String getMessage() {
            return message;
        }
    
    }

    消息发送工具/通道

    class Channel{
        // 这是一个静态变量(属于该类的全局变量)
        private static MessageEntity messageEntity;
        public static void setMessageEntity(MessageEntity message){
            messageEntity = message;
        }
        public static void sendMessage(){
            System.out.println("["+Thread.currentThread().getName()+"消息发送:]" + messageEntity.getMessage());;
        }
    }

    定义主方法

    public class ThreadLocalDemo {
    
        public static void main(String[] args){
            Thread thread1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    Channel.setMessageEntity(new MessageEntity("这是线程1的消息"));
                    Channel.sendMessage();
                }
            },"线程1");
            Thread thread2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    Channel.setMessageEntity(new MessageEntity("这是线程2的消息"));
                    Channel.sendMessage();
                }
            },"线程2");
            Thread thread3 = new Thread(new Runnable() {
                @Override
                public void run() {
                    Channel.setMessageEntity(new MessageEntity("这是线程3的消息"));
                    Channel.sendMessage();
                }
            },"线程3");
    
            thread1.start();thread2.start();thread3.start();
        }
    }

    执行效果:

    由于消息发送的通道的MessageEntity属性是被static修饰的,说明它是类成员变量,所以在某个线程修改了这个成员变量的值,在执行发送之前Channel.sendMessage(),被其他线程修改了,就会导致下面的结果,当前线程发送了别的线程的消息;这就产生了并发的数据问题;

    private static MessageEntity messageEntity;

    使用ThreadLocal改进:

    在不改变预定结构的情况下,我们可以使用ThreadLocal来避免这种并发问题,将类成员变量拷贝到各自的线程里;

    1. 将MessageEntity 使用ThreadLocal的方式进行声明;

    2. 使用set方法复制副本;

    3. 使用get方法得到副本;

    class Channel{
        private static final ThreadLocal<MessageEntity> MESSAGE_LOCAL = new ThreadLocal<>();
        public static void setMessageEntity(MessageEntity message){
            MESSAGE_LOCAL.set(message);
        }
        public static void sendMessage(){
            System.out.println("["+Thread.currentThread().getName()+"消息发送:]" + MESSAGE_LOCAL.get().getMessage());;
        }
    }

    修改后每次执行的效果都是正确的:

     

    三、其他补充:

    1. 必须回收自定义的ThreadLocal变量,尤其在线程池场景下,线程经常会被复用,如果不清理自定义的 ThreadLocal变量,可能会影响后续业务逻辑和造成内存泄露等问题。尽量在代理中使用try-finally块进行回收。《阿里巴巴开发手册》

    objectThreadLocal.set(userInfo);
    try {
        // ...
    } finally {
        objectThreadLocal.remove();
    }

    2. ThreadLocal和Synchronized都是为了解决多线程中相同变量的访问冲突问题,不同的点是:

    • Synchronized是通过线程等待,牺牲时间来解决访问冲突
    • ThreadLocal是通过每个线程单独一份存储空间,牺牲空间来解决冲突,并且相比于Synchronized,ThreadLocal具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值。


    边系鞋带边思考人生.
  • 相关阅读:
    Javascript中String()和new String()的区别——JS的包装对象
    文言色彩的客套话之感想
    面试时候可以问的问题集锦
    ES6的原始类型数据——Symbol
    python
    python
    python
    python
    python
    python
  • 原文地址:https://www.cnblogs.com/crazytrip/p/14870537.html
Copyright © 2011-2022 走看看