在并发编程中,为了线性安全我们经常要使用各种各样的“锁”。 不管锁的粒度如何小,哪怕是CAS操作,都存在先来后到的排队问题。有时候我们只是单纯地计数,没有太复杂的操作,想要一种不涉及锁的线程安全的操作——ThreadLocal就是一种无锁的线程安全的设计。
当使用ThreadLocal修饰变量时,ThreadLocal为每个线程(thread)设置了一个独立的局部(local)变量副本,每个线程的变量副本互不干扰,从而实现了线程安全性。
我们创建一个ThreadLocal变量count,用两个不同的线程使用同一个变量,并且一个快一个慢(sleep100ms 和sleep 200ms)看它们能否各自独立计数。
public class MainTest { private static ThreadLocal<Integer> count = new ThreadLocal<Integer>(){ @Override protected Integer initialValue() { //重写了initialValue 为了给count重初值 return new Integer(0); } }; public static int getNext(){ int data = count.get() + 1; count.set(data); return data; } public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { while (true){ try { System.out.println(Thread.currentThread().getName() + " " + getNext()); Thread.sleep(100); //这个线程每次sleep 100毫秒 } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { while(true){ try { System.out.println(Thread.currentThread().getName() + " " + getNext()); Thread.sleep(200); //这个线程每次sleep 200毫秒 } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } }运行结果
可以看到两个线程使用了同一个ThreadLocal变量,各自独立计数一个快一个慢,且没有出现线程安全性问题。
ThreadLocal使用时要先用set()再用get(), 因为默认的initialValue()里value的值设置为null,如果不先set()就要重写initialValue()方法,否则会抛nullPointerException。
ThreadLocal<T> 类有一个静态内部类ThreadLocalMap,ThreadLocalMap里有一个Entry内部类组成的数组。
ThreadLocal通过get()与set()方法来获取和存储变量数据到数组中。
set()方法
当一个线程中调用了ThreadLocal的set方法时,首先获取本线程的threadID,然后获取本线程内部的ThreadLocalMap,用ThreadLocal值和value创始一个Entry对象存入自身所在的ThreadLocalMap中。因此,各个线程的ThreadLocal和value值都存储在各线程内部,互不干扰。
get()方法
get方法与set方法类似。当一个线程调用了ThreadLocal的get方法时,获取本线程的threadID和本线程内部的ThreadLocalMap,然后取出Entry对象获存储的值。也是各线程互不干扰。