ThreadLocal:线程局部变量,是一种多线程之间并发访问变量的解决方案。与synchronized等加锁的方式不同,ThreadLocal完全不提供锁,其采用以空间换时间的思想,为每个线程的变量提供独立副本以保障线程安全。在并发不是很高的业务场景下,加锁较之会性能更好,但ThreadLocal作为一套与锁完全无关的线程安全解决方案,在高并发量或者竞争激烈的场景下,其可以在一定程度上减少锁竞争。下面给出一小段例子:
package _08_ThreadLocal与单例模型;
public class ThreadLocal01 {
public static ThreadLocal<String> thread = new ThreadLocal<String>();
@SuppressWarnings("static-access")
public void getThread() {
System.err.println(Thread.currentThread().getName()+":"+this.thread.get());
}
public void setThread(String value) {
thread.set(value);
}
public static void main(String[] args) {
final ThreadLocal01 thread01 = new ThreadLocal01();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
thread01.setThread("张三");
thread01.getThread();
}
},"thread1");
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3*1000);
thread01.getThread();
} catch (InterruptedException e) {
System.err.println(e.getMessage());
}
}
},"thread2");
thread1.start();
thread2.start();
}
}
运行起来,显然,只有线程thread1输出值“张三”,而线程thread2输出为null,下面进行源码分析:
代码段A:
/**
* Returns the value in the current thread's copy of this thread-local variable.
* If the variable has no value for the current thread, it is first initialized to the
value returned by an invocation of the {@link #initialValue} method.
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
当执行“thread.get()”时进入上面A,A先是获取当前线程,然后进行getMap(t)获取ThreadLocalMap,此处thread1的Map存在值“张三”,而thread2则是初次进入,其Map此次需要先进行初始化,其值为空。
ThreadLocalMap的说明为:
/**
* The entries in this hash map extend WeakReference, using its main ref field as
the key (which is always a ThreadLocal object).
* Note that null keys (i.e. entry.get() == null) mean that the key is no longer
referenced, so the entry can be expunged from table.
* Such entries are referred to as "stale entries" in the code that follows.
*/
thread1获得其ThreadLocalMap后通过getEntity(this)获取其对应的Entity(Entity为ThreadLocalMap-Object键值对),然后返回其对应的value(即“张三”),若没有对应的ThreadLocalMap,则返回setInitialValue()的返回值(即初始化一个ThreadLocalMap)。
setInitialValue的源码:
/**
* Variant of set() to establish initialValue.
* Used instead of set() in case user has overridden the set() method.
*
* @return the initial value
*/
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
ThreadLocal与单例模式的结合:
单例模式分为懒汉模式和饿汉模式,与ThreadLocal的结合主要采用dubble checkInstance和static inner class两种方式。下面给出两段小例子进行演示。
dubble checkInstance方式:
public class DoubleSingleton {
private static DoubleSingleton ds;
public static DoubleSingleton getDs() {
if(ds == null) {
try {
System.err.println("初始化对象");
Thread.sleep(2*1000);
}catch(Exception e) {
System.err.println(e.getMessage());
}
synchronized (DoubleSingleton.class) {
if(ds == null) {
ds = new DoubleSingleton();
}
}
}
return ds;
}
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.err.println(DoubleSingleton.getDs().hashCode());
}
},"thread1");
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
System.err.println(DoubleSingleton.getDs().hashCode());
}
},"thread2");
Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
System.err.println(DoubleSingleton.getDs().hashCode());
}
},"thread3");
thread1.start();
thread2.start();
thread3.start();
}
}
static inner class方式:
public class Singleton {
private static class InnerSingleton{
private static Singleton single = new Singleton();
}
public static Singleton getInstance() {
return InnerSingleton.single;
}
}