看到很多框架中都使用了ThreadLocal ,单从名字来说很可能把他理解成为一个“本地线程”之类的玩意儿。。。
先上代码:
package com.tiger.Thread.concurrent; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class testLocalThread { private ThreadLocal<Integer> i = new ThreadLocal<Integer>(){ @Override protected Integer initialValue() { return 0; } }; public void add(int num){ i.set(i.get().intValue()+num); } public int getI(){ return i.get(); } /*private int i = 0; public synchronized void add(int num){ i += num; } public synchronized int getI(){ return i; }*/ public static void main(String[] args) throws InterruptedException { final testLocalThread t = new testLocalThread(); ExecutorService es = Executors.newFixedThreadPool(2); for(int i=0;i<2;i++){ es.execute(new Runnable() { @Override public void run() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } t.add(1); System.out.println(t.getI()); } }); } es.shutdown(); es.awaitTermination(2, TimeUnit.SECONDS); } }
先从结果来分析:若用ThreadLocal,多个线程执行后,每个线程的输出都为1,
而直接使用int 变量则会看到计算结果是线程间相互作用的,而且如果不对普通变量进行同步控制还会出现意料之外的结果。
当我们说线程安全的时候其实是说某个状态在线程之间的一致性。更直接的说就是多个线程在同一时刻所看到的变量状态是一致的,但是在java的内存模型里我们可以看到变量对于线程是有可见性的,也就是说某一时刻当变量状态对其他线程不可见时,那么其他线程在不知情的情况下看到和使用的很有可能是一个过期的状态。
在Java Concurrency In Practice中对线程安全有几个建议:
1.无状态对象一定是线程安全的
2.不可变对象一定是线程安全的
3.用锁来保证互斥性和可见性
4.保障线程安全的2个整体思路:线程封闭和安全发布
回到我们的例子正是对这2个思路的体现:
ThreadLocal是见变量封闭在各自线程内部(具体实现参见源码)并未进行发布,对外没有状态,也自然就没有所谓的同步控制一说。
而一般变量当调用add()或者get()方法时实际上便将状态发布了出来,所有线程均可使用。当多个线程使用一个有状态并且可以改变的变量时,就必须考虑安全发布这个变量,需要用锁来保证变量的可见性(当然可以用atomic类型来进行安全发布避免使用锁带来的竞争)。
之前看到网上有不少文章都将ThreadLocal和数据在线程间的共享拉上了关系,个人认为可能会小小的对理解产生偏差。ThreadLocal只是让变量在各个线程间属性和行为一致,跟状态共享没有关系。
时间: 2024-11-05 21:58:09