ThreadLocal
主要解决多线程并发访问致使数据不一致问题。 ThreadLocal
为每个使用该变量的线程都提供一个变量值的副本,虽然这种方式耗费内存,可是大大减小了线程同步所带来性能消耗,也减小了线程并发控制的复杂度。java
举个例子:将
ThreadLocal
比喻成存放数据的盒子,盒子中只能够存储盒子主人的私有数据。因为数据空间是隔离的,所以,多线程并发访问而互不影响,从而避免了线程安全的问题。安全
ThreadLocal 是如何作到为每个线程维护变量的副本?其实实现思路很简单,在ThreadLocal
类中有一个ThreadLocalMap
,用于存储每个线程的变量的副本。bash
###ThreadLocal和Synchonized 的区别多线程
ThreadLocal
:使用副本机制,使每一个线程在某一时该访问到的对象是不一样的,从而保证线程间的数据隔离。synchronized
:使用锁机制,使对象在某一时只能被一个线程访问,从而保证线程间的数据共享。import java.util.*;
public class ThreadLocalDemo implements Runnable {
// list 不是线程安全的,因此每一个线程都要有本身独立的副本
private static ThreadLocal<List> threadLocal = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
ThreadLocalDemo obj = new ThreadLocalDemo();
for (int i = 0; i < 10; i++) {
Thread t = new Thread(obj, "thread" + i);
Thread.sleep(1000);
t.start();
}
}
@Override
public void run() {
List<String> list = new ArrayList<>();
list.add(Thread.currentThread().getName());
threadLocal.set(list);
System.out.println(Thread.currentThread().getName() + ":" + threadLocal.get());
list.add("1");
list.add("2");
System.out.println(Thread.currentThread().getName() + ":" + threadLocal.get());
}
}
复制代码
get()
方法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();
}
复制代码
其中ThreadLocalMap
能够理解为定制化的HashMap
,而ThreadLocal
只是对其进行了封装,并传递变量值,经过ThreadLocalMap
的get()
方法获取当前线程局部变量的值。并发
set()
方法public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
复制代码
实现原理和get()
方法同样,内部由ThreadLocalMap
管理数据,set 时先检查 map 是否为空,若是不为空就直接保存数据,若是为空则先建立 map 再保存数据。ide
ThreadLocalMap
中使用的 key 为弱引用,而 value 是强引用。因此,若是ThreadLocal
没有被外部强引用的状况下,在GC 执行的时候会把 key 清理掉,而 value 不会被清理。这样一来,ThreadLocalMap
中就会出现 key 为 null 的 Entry。假如不作任何措施的话,value 可能永远没法被 GC 回收,这个时候可能会产生内存泄漏。性能
以上状况,ThreadLocalMap
的实现已经考虑到了,在调用 set()
、get()
和remove()
方法时,会清理 key 为 null 的记录。(咱们在使用完 ThreadLocal 后,最好手动调用 remove() 方法)ui
弱引用
若是一个对象只具备弱引用,那就属于无关紧要的对象。弱引用与软引用的区别在于:只具备弱引用的对象拥有更短暂的生命周期。垃圾回收器线程执行时会扫描它所管辖的内存区域,一旦发现只具备弱引用的对象,无论当前内存空间足够与否,都会将它回收。不过,因为垃圾回收期是一个优先级很低的线程,所以不必定很快就会发现那些只具备弱引用的对象。this
弱引用能够和一个引用队列(
ReferenceQueue
)联合使用,若是弱引用对象被垃圾回收后,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。spa