JDK的InheritableThreadLocal类可以完成父线程到子线程的值传递。但对于使用线程池等会池化复用线程的组件的情况,线程由线程池创建好,并且线程是池化起来反复使用的;这时父子线程关系的ThreadLocal值传递已经没有意义,应用需要的实际上是把任务提交给线程池时的ThreadLocal值传递到任务执行时。
public class TransmittableThreadLocal<T> extends InheritableThreadLocal<T> { ...... }
首先TransmittableThreadLocal继承自InheritableThreadLocal,这样可以在不破坏原有InheritableThreadLocal特性的情况下,还能充分使用Thread线程创建过程中执行init方法,从而达到父子线程传递数据的目的。
holder中存放的是InheritableThreadLocal本地变量
WeakHashMap支持存放空置
//?理解holder,需注意如下几点:
// 1、holder 是 InheritableThreadLocal 变量;
// 2、holder 是 static 变量;
// 3、value 是 WeakHashMap;
// 4、深刻理解 ThreadLocal 工作原理;
private?static?InheritableThreadLocal<Map<TransmittableThreadLocal<?>,??>>?holder?=
????new?InheritableThreadLocal<Map<TransmittableThreadLocal<?>,??>>()?{
??????@Override
??????protected?Map<TransmittableThreadLocal<?>,??>?initialValue()?{
??????????return?new?WeakHashMap<>();
??????}
??????@Override
??????protected?Map<TransmittableThreadLocal<?>,??>?childValue(Map<TransmittableThreadLocal<?>,??>?parentValue)?{
??????????return?new?WeakHashMap<>(parentValue);
??????}
};
get方法调用时,先获取父类的相关数据判断是否有数据,然后在holder中把自身也给加进去
set方法调用时,先在父类中设置,再本地判断是holder否为删除或者是新增数据
remove调用时,先删除自身,再删除父类中的数据,删除也是直接以自身this作为变量Key
//?调用?get()?方法时,同时将?this?指针放入?holder
public?final?T?get()?{
????T?value?=?super.get();
????if?(null?!=?value)?{
????????addValue();
????}
????return?value;
}
void?addValue()?{
????if?(!holder.get().containsKey(this))?{
????????holder.get().put(this,?null);?//?WeakHashMap?supports?null?value.
????}
}
//?调用?set()?方法时,同时处理?holder?中?this?指针
public?final?void?set(T?value)?{
????super.set(value);
????if?(null?==?value)?{?//?may?set?null?to?remove?value
????????removeValue();
????}?else?{
????????addValue();
????}
}
void?removeValue()?{
????holder.get().remove(this);
}
?
先取得holder
备份线程本地数据
run原先的方法
还原线程本地数据
public?void?run()?{
?????Object?captured?=?this.capturedRef.get():
?????if?(captured?!=?null?&&?(!this.releaseTtlValueReferenceAfterRun?||
?????????????this.capturedRef.compareAndSet(captured,?(0bject)?null))){
?????????Object?backup?=?Transmitter.replay(captured);
?????????try?{
?????????????this.runnable.run();
?????????}?finally?{
?????????????Transmitter.restore(backup);
?????????}
?????}else{
?????????throw?new?IllegalStateException("TTL?value?reference?is?released?after?run!");
?????}
}
?
先获取holder中的数据
进行迭代,数据在captured中不存在,但是holder中存在,说明是后来加进去的,进行删除
再将captured设置到当前线程中
public?static?Object?repLay(@Nonnull?Object?captured)?(
????Map<TransmittableThreadLocal<?>,Object>capturedMap = (Map) captured;
????Map<TransmittableThreadLocal<?>,Object>backup = new?HashMap()?;
????Iterator?iterator=((Map)?TransmittableThreadLocal.holder.get()).entrySet().iterator();
????while(iterator.has?Next()?)?(
???????Entry<TransmittableThreadLocal<?>,?>next = (Entry)?iterator.next()
???????TransmittableThreadLocal<?>threadLocal = (TransmittableThreadLocal)next.getKey();
???????backup.put(threadLocal,?threadLocal.get()?)?;
???????if(!capturedMap.containsKey(thread?Local)?)?(
?????????? iterator.remove()?;
???????????threadLocal.superRemove()?;
???????)
????setTtlValuesTo(capturedMap)?;
????TransmittableThreadLocal.doExecuteCallback(true)?;
????return?backup;
)
?
先获取holder中的数据
backup中不存在,holder中存在,说明是后面加进去的,进行删除还原操作
再将backup设置到当前线程中
public?static?void?restore(@Nonnull0bject?backup)?(
????Map<TransmittableThreadLocal<?>,Object>?backupMap?=?(Map)backup;
????TransmittableThreadLocal.doExecuteCallback(false)
????Iterator?iterator=((Map)?TransmittableThreadLocal.holder.get()).entrySet().iterator();
????while(iterator.has?Next()?)?(
????????Entry<TransmittableThreadLocal<?>,?>?next?=?(Entry)iterator.next();
????????TransmittableThreadLocal<?>?threadLocaL=(TransmittableThreadLocal)next.getKey();
????????if(!backupMap.containsKey(threadLocal)?)?(
??????????? iterator.remove()?;
??????????? threadLocal.superRemove()?;
????????}
????|
??? setTtlValuesTo(backupMap)?;
}
1、相同点
? ?都可以实现线程间的等待
2、不同点
? ?侧重点不同
CountDownLatch:一般用于一个线程等待一组其它线程;计数器不可以重用
CyclicBarrier:一般是一组线程间的相互等待至某同步点,计数器是可以重用的
?? 实现原理不同
CyclicBarrier:如果有三个线程thread1、thread2和thread3,假设线程执行顺序是thread1、thread2、thread3,那么thread1、thread2对应的Node节点会被加入到Condition等待队列中,当thread3执行的时候,会将thread1、thread2对应的Node节点按thread1、thread2顺序转移到AQS同步队列中,thread3执行lock.unlock()的时候,会先唤醒thread1,thread1恢复继续执行,thread1执行到lock.unlock()的时候会唤醒thread2恢复执行
CountDownLatch:使用CountDownLatch(int count)构造器创建CountDownLatch实例?,将count参数赋值给内部计数 state,调 await() 法阻塞当前线程,并将当前线程封装加到等待队 中,直到state等于零或当前线程被中断;调 countDown() 法使state值减 ,如果state等于零则唤醒等待队中的线程
?
AQS是一个基于状态(state)的链表管理方式,reentracntlock这个锁是基于AQS实现的子类sync这个来完成锁。
获取锁的时候,当前线程会去更新状态state的值,如果为0才去更新,通过CAS进行更新,如果成功更新为1,那么获取到锁,将锁的拥有者改成当前线程,如果失败,那么进行tryAcquire() 这个函数进行首先还是尝试更新state状态,反正开销也小,再次去尝试一次也行,如果尝试失败,那么去看看当前拥有锁的线程是不是当前线程,如果是,那么将state状态值加1,如果不是,那么将线程入阻塞队列,addWaiter函数,进行的话首先判断当前head是不是为空,为空尝试将当前的线程关联的节点用CAS加入队列,不为空或者加入失败,那么用CAS加入到队列的下一个节点。
释放锁的时候,将状态值减1,如果状态值为0说明可以释放锁,如果结果状态为0,就将排它锁的Owner设置为null,以使得其它的线程有机会进行执行。
?
1、什么是内存屏障
内存屏障其实就是一个CPU指令,在硬件层面上来说可以扥为两种:Load Barrier 和 Store Barrier即读屏障和写屏障。主要有两个作用:
(1)阻止屏障两侧的指令重排序;
(2)强制把写缓冲区/高速缓存中的脏数据等写回主内存,让缓存中相应的数据失效。
?
在JVM层面上来说作用与上面的一样,但是种类可以分为四种:
?
首先一个变量被volatile关键字修饰之后有两个作用:
(1)对于写操作:对变量更改完之后,要立刻写回到主存中。
(2)对于读操作:对变量读取的时候,要从主内存中读,而不是缓存。
现在针对上面JVM的四种内存屏障,应用到volatile身上。因此volatile也带有了这种效果。其实上面提到的这些内存屏障应用的效果,可以用happen-before来总结归纳。
?
内存屏障有三种类型和一种伪类型:
(1)lfence:即读屏障(Load Barrier),在读指令前插入读屏障,可以让高速缓存中的数据失效,重新从主内存加载数据,以保证读取的是最新的数据。
(2)sfence:即写屏障(Store Barrier),在写指令之后插入写屏障,能让写入缓存的最新数据写回到主内存,以保证写入的数据立刻对其他线程可见。
(3)mfence,即全能屏障,具备ifence和sfence的能力。
(4)Lock前缀:Lock不是一种内存屏障,但是它能完成类似全能型内存屏障的功能。
?
为什么说Lock是一种伪类型的内存屏障,是因为内存屏障具有happen-before的效果,而Lock在一定程度上保证了先后执行的顺序,因此也叫做伪类型。比如,IO操作的指令,当指令不执行时,就具有了mfence的功能。
?
由于内存屏障的作用,避免了volatile变量和其它指令重排序、线程之间实现了通信,使得volatile表现出了锁的特性。
原文:https://blog.51cto.com/14801695/2499336