ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。当使用ThreadLocal维护变量时,ThreadLocal为每一个变量的线程提供独立的变量副本,因此每个线程均可以独立地改变本身的副本,而不会影响其它线程所对应的副本。安全
ThreadLocal类接口很简单,只有4个方法,咱们先来了解一下:多线程
•void set(Object value)设置当前线程的线程局部变量的值。并发
•public Object get()该方法返回当前线程所对应的线程局部变量。this
•public void remove()将当前线程局部变量的值删除,目的是为了减小内存的占用,该方法是JDK 5.0新增的方法。须要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,因此显式调用该方法清除线程的局部变量并非必须的操做,但它能够加快内存回收的速度。spa
•protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,而且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。线程
ThreadLocal是如何作到为每个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。咱们本身就能够提供一个简单的实现版本:设计
package com.wxp.learn;
public class Test02 {
//经过匿名内部类覆盖ThreadLocal的initialValue方法,指定初始化
private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){
public Integer initialValue(){
return 0;
}
};
//获取下一个序列值
public int getNextNum(){
seqNum.set(seqNum.get()+1);
return seqNum.get();
}
public static void main(String[] args)throws Exception {
// TODO Auto-generated method stub
Test02 sn = new Test02();
//3个线程共享sn,各自产生序列号
TestClient t1 = new TestClient(sn);
TestClient t2 = new TestClient(sn);
TestClient t3 = new TestClient(sn);
t1.start();t2.start();t3.start();
//阻塞,使main不退出
System.in.read();
}
private static class TestClient extends Thread{
private Test02 sn;对象
public TestClient(Test02 sn) {
super();
this.sn = sn;
}
public void run(){
for (int i = 0; i <3; i++) {
//每一个线程打印出3个序列值
System.out.println("thread["+Thread.currentThread().getName()
+"]-->sn["+sn.getNextNum()+"]");
}
}
}
}接口
执行输出内容:内存
thread[Thread-2]-->sn[1]
thread[Thread-0]-->sn[1]
thread[Thread-1]-->sn[1]
thread[Thread-0]-->sn[2]
thread[Thread-2]-->sn[2]
thread[Thread-0]-->sn[3]
thread[Thread-1]-->sn[2]
thread[Thread-2]-->sn[3]
thread[Thread-1]-->sn[3]
由此咱们会发现每一个线程所产生的序号虽然都共享同一个Test02实例,但它们并无发生相互干扰的状况,而是各自产生独立的序列号,这是由于咱们经过ThreadLocal为每个线程提供了单独的副本。
Thread同步机制的比较
ThreadLocal和线程同步机制相比有什么优点呢?ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。
在同步机制中,经过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析何时对变量进行读写,何时须要锁定某个对象,何时释放对象锁等繁杂的问题,程序设计和编写难度相对较大。
而ThreadLocal则从另外一个角度来解决多线程的并发访问。ThreadLocal会为每个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。由于每个线程都拥有本身的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,能够把不安全的变量封装进ThreadLocal。