一、对比:
ThreadLocal和线程同步都是为了解决多线程中同一变量的访问冲突问题,那么,二者的区别在哪里?和线程同步机制对比,ThreadLocal有什么优势?
- 同步机制中通过对象的锁机制保证同一时间只有一个线程来访问变量,这是该变量是多个线程共享的,使用同步机制要缜密的分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放掉锁等复杂的问题,程序设计和编写难度大
- ThreadLocal则为每一个线程提供了一个独立的变量副本,从而隔离了多个线程对访问数据的冲突。因为每一个线程都有自己的变量副本,从而也就没有必要进行同步了。ThreadLocal提供了线程安全的对象的封装,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
- 由于ThreadLocal可以持有任意类型的对象,低版本的jdk所提供的get()方法的返回值是object类型的。需要进行强制类型转换。但是jdk5.0以后就是用了泛型从而解决了该问题。
- 总的来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间的”方式——访问串行化,对象共享化。而ThreadLocal则采用了“以空间换时间”的方式:访问并行化,对象独享化。前者仅提供一份变量,让不同的线程队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
二、Thread的同步机制:(也可以通过ReentrantLock)
在普通的同步机制中,是通过对象加锁来实现多个线程对统一变量的安全访问的,这时该变量是多个线程共享的,使用这种同步机制需要很细致的分析在什么时候对变量进行读写、什么时候需要锁定某个对象,什么时候释放该对象的锁等等。同步机制中一般使用synchronized关键字来保证同一时刻只有一个线程对共享变量进行操作。但在有些情况下,synchronized不能保证多线程对共享变量的正确读写。例如类有一个类变量,该类变量会被多个类方法读写,当多线程操作该类的实例对象时,如果线程对类变量有读取、写入操作就会发生类变量读写错误,即便是在类方法前加上synchronized也无效,因为同一个线程在两次调用方法之间时锁是被释放的,这时其它线程可以访问对象的类方法,读取或修改类变量。 这种情况下可以将类变量放到ThreadLocal类型的对象中,使变量在每个线程中都有独立拷贝,不会出现一个线程读取变量时而被另一个线程修改的现象。
三、ThreadLocal:
Threadlocal从另一个角度来解决多线程的并发访问,threadlocal为每一个线程维护一个和该线程绑定的变量的副本,使每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。threadlocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的整个变量封装进threadlocal,或者把该对象的特定的线程的状态封装进threadlocal。
四、总结:
ThreadLocal并不能替代同步机制,两者面向的问题领域不同。
同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式;
而threadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享变量,这样当然不需要对多个线程进行同步了。
所以,如果需要进行多个线程之间进行通信,则使用同步机制;如果需要隔离多个线程之间的共享冲突,可以使用threadLocal。这将极大的简化程序,使程序更加易读、简洁。
ThreadLocal是解决线程安全问题一个很好的思路.
它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。
下面提供一个简单的ThreadLocal的实现版本:
package com.wqc.realize;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class MyThreadLocal {
private Map valueMap = Collections.synchronizedMap(new HashMap<>());
/**
* 键为线程对象,值为本线程的变量副本
* @param newValue
*/
public void set(Object newValue) {
valueMap.put(Thread.currentThread(), newValue);
}
public Object get() {
Thread currentThread = Thread.currentThread();
Object o = valueMap.get(currentThread); //返回本线程对应的变量
if (o == null && !valueMap.containsKey(currentThread)) {
o = initialValue();
valueMap.put(currentThread, o); //不存在则在Map中保存起来
}
return o;
}
public void remove() {
valueMap.remove(Thread.currentThread());
}
public Object initialValue() {
return null;
}
}
图中代码所示的思路和JDK所提供的 ThreadLocal类在实现思路上是很接近的。