简介
如果仅在单线程内访问数据,就不需要同步,这种技术被称为线程封闭,它是实现线程安全性最简单方式之一。 当某个对象封闭在一个线程中时,这种用法将自动实现线程安全性,即使被封闭的对象本身不是线程安全的。
- Swing的可视化组件和数据模型对象都不是线程安全的,Swing通过将它们封闭到了Swing的事件分发线程中来实现线程安全性。
- JDBC Connection对象并没规定其必须是线程安全的,但是Connection连接在某个线程中使用时,在其归还之前,连接池并不会将它分给别的线程,这种连接管理模式隐含的将Connection对象封闭在线程中。
1.Ad-hoc线程封闭
Ad-hoc线程封闭是指,维护线程封闭性的职责完全由程序实现来承担,Ad-hoc线程封闭是非常脆弱的,因为没有任何一种语音特性。
2.栈封闭
在栈封闭中,只能通过局部变量才能访问对象。其比Ad-hoc线程封闭更易于维护,也更加健壮。
如下示例代码,numPairs不会破坏线程的安全性,其是基本局部变量,其他线程根本无法访问到。栈封闭要注意内部引用不要溢出
public int loadTheArk(Collection<Animal> candidates){
SortedSet<Animal> animals;
int numPairs = 0;
Animal candidate = null;
//animals 被封闭在方法中,不要是他们溢出
animals = new TreeSet<Animal>();
animals.addAll(candidates);
//对封闭的animals进行处理
....
return numPairs;
}
3.ThreadLocal线程变量
threadlocal类用来提供线程内部的局部变量,这种变量在多线程环境下访问时能保证各个线程里的变量相对独立于其他线程内的变量,ThreadLocal实例通常来说都是private static类型的,用于关联线程和线程的上下文。
ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递复杂度
private static ThreadLocal<String> threadLocal = new ThreadLocal() {
//重写初始化方法
@Override
public String initialValue() {
return Person.getName();
}
};
static class Person {
private static String name = "name";
private static int i=0;
public static String getName() {
return name+i++;
}
}
ThreadLocal类实现:
开发寻址法实现的散列表
static class ThreadLocalMap {
//弱引用ThreadLocal,线程执行完毕后,将不在引用threadlocal,当GC发///现此弱引用时就会被回收
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//根据threadLocal获取此Entry
private Entry getEntry(ThreadLocal<?> key) {
//此处可以看出,i 是nextHashCode
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
//进行遍历table查找是否存在 ThreadLocal 引用,并释放key=null的table所占内存
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal<?> k = e.get();
if (k == key)
return e;
//key=null,释放table[i]所占内存防止无效堆占用,导致内存泄漏
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
private Entry[] table;
}
//获取hashCode的方法
private final int threadLocalHashCode = nextHashCode();
private static AtomicInteger nextHashCode =
new AtomicInteger();
//此值能够产生均匀的散列序列
private static final int HASH_INCREMENT = 0x61c88647;
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
1.GET方法
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
//此线程绑定的已有localMap
if (map != null) {
//获取
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//设置此线程的变量
return setInitialValue();
}
//获取当前线程上的LocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
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;
}
--------------------------------------------------------
Thread类:
public class Thread implements Runnable {
//Thread类内保存有此现场的LoalMap
ThreadLocal.ThreadLocalMap threadLocals = null;
}
--------------------------------------------------------
场景分析:
- 多个线程引用一个ThreadLocal
此时每个线程都会由ThreadLocal给他们创建自己的LocalMap(createMap),并且直接引用,
这些线程的localMap key值又是同一个threadLocal,value为初始化的新值,
Entry中ThreadLocal为弱引用,线程结束时将失去对其的强引用(ThreadLocal,ThreadLocalMap),此时GC发现会直接回收他们,但是key=null,value并未被回收,可能会引发内存溢出,ThreadLocal类对其提供了检测实现
graph LR
Thread1-->ThreadLocal1
Thread2-->ThreadLocal1
Thread3-->ThreadLocal1
ThreadLocal1-->ThreadLcoalMap1
ThreadLocal1-->ThreadLcoalMap2
ThreadLocal1-->ThreadLcoalMap3
ThreadLcoalMap–>Entry[](ThreadLocal
graph LR
Thread1-->ThreadLocal1
Thread1-->ThreadLocal2
Thread1-->ThreadLocal3
第一个threadlocal1.get()时,会为thread1 create threadLocalMap,
下面的ThreadLocal2.get(),会调用threadLocalMap.set(ThreadLocal2, value);以此类推。