zoukankan      html  css  js  c++  java
  • ThreadLocal的使用和理解

    ThreadLocal是个threadlocalvariable(线程局部变量),其实就是为每一个使用该变量的线程都提供一个变量值的副本,从线程的角度看,每个线程都保持一个对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。

    ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。
     
    概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
     
    实例:
    public class Student {
        private int age = 0;   //年龄
     
        public int getAge() {
            return this.age;
        }
     
        public void setAge(int age) {
            this.age = age;
        }
    }
    
    public class ThreadLocalDemo implements Runnable {
        //创建线程局部变量studentLocal,在后面你会发现用来保存Student对象
        private final static ThreadLocal studentLocal = new ThreadLocal();
     
        public static void main(String[] agrs) {
            ThreadLocalDemo td = new ThreadLocalDemo();
            Thread t1 = new Thread(td, "a");
            Thread t2 = new Thread(td, "b");
            t1.start();
            t2.start();
        }
     
        public void run() {
            accessStudent();
        }
     
        /**
         * 示例业务方法,用来测试
         */
        public void accessStudent() {
            //获取当前线程的名字
            String currentThreadName = Thread.currentThread().getName();
            System.out.println(currentThreadName + " is running!");
            //产生一个随机数并打印
            Random random = new Random();
            int age = random.nextInt(100);
            System.out.println("thread " + currentThreadName + " set age to:" + age);
            //获取一个Student对象,并将随机数年龄插入到对象属性中
            Student student = getStudent();
            student.setAge(age);
            System.out.println("thread " + currentThreadName + " first read age is:" + student.getAge());
            try {
                Thread.sleep(500);
            }
            catch (InterruptedException ex) {
                ex.printStackTrace();
            }
            System.out.println("thread " + currentThreadName + " second read age is:" + student.getAge());
        }
     
        protected Student getStudent() {
            //获取本地线程变量并强制转换为Student类型
            Student student = (Student) studentLocal.get();
            //线程首次执行此方法的时候,studentLocal.get()肯定为null
            if (student == null) {
                //创建一个Student对象,并保存到本地线程变量studentLocal中
                student = new Student();
                studentLocal.set(student);
            }
            return student;
        }
    }

    运行结果:

    a is running!
    thread a set age to:76
    b is running!
    thread b set age to:27
    thread a first read age is:76
    thread b first read age is:27
    thread a second read age is:76
    thread b second read age is:27

    ThreadLocal和synchronized的比较:

    ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区 别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本, 使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通 信时能够获得数据共享。
     
    Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
     
    当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂。
     
    ThreadLocal的一般使用步骤:
    1、在多线程的类(如ThreadDemo类)中,创建一个ThreadLocal对象threadXxx,用来保存线程间需要隔离处理的对象xxx。
    2、在ThreadDemo类中,创建一个获取要隔离访问的数据的方法getXxx(),在方法中判断,若ThreadLocal对象为null时候,应该new()一个隔离访问类型的对象,并强制转换为要应用的类型。
    3、在ThreadDemo类的run()方法中,通过getXxx()方法获取要操作的数据,这样可以保证每个线程对应一个数据对象,在任何时刻都操作的是这个对象。
  • 相关阅读:
    mysql: 多时区的聚合统计
    机器学习实例---3.2、朴素贝叶斯之新浪新闻分类
    机器学习实例---3.1、朴素贝叶斯基础
    机器学习实例---2.1、决策树(实战)
    python的pickle模块
    机器学习实例---2.1、决策树(介绍)
    如何计算熵
    sklearn库简单介绍
    机器学习实例---1.3、k-近邻算法(数字识别)
    理解ThreadPoolExecutor线程池的corePoolSize、maximumPoolSize和poolSize
  • 原文地址:https://www.cnblogs.com/shilin000/p/4788502.html
Copyright © 2011-2022 走看看