Latch、Barrier、ThreadLocal

1,Latch

       java.util.concurrent.CountDownLatch经常被称为闭锁,它能够使指定线程等待计数线程完成各自工作后再执行。如果提供一个计数为2的CountDownLatch,每执行完一个线程就调用latch的countDown方法把计数器减1,等全部任务执行完成后,latch.await()之后的代码才会执行。

       CountDownLatch提供了对一组线程任务进行约束的能力,也就是说可以在任务中灵活的根据条件来调用latch#countDown()方法,从而决定是否中断CountDownLatch#await造成的阻塞。这种优秀的能力也是由AQS实现的。

       tryAcquireShared验证失败则阻塞队列,验证成功则中断阻塞,它的返回值会增加等待队列中的线程数。

       tryReleaseShared则会释放等待队列中的线程。

       CountDownLatch正式利用了这两个特性,通过对State和count进行绑定,用它的计数器来代替AQS中的等待线程计数,然后,提供封装了AQS的对外使用入口。

       需要注意的是,一个CountDownLatch被使用后,它的计数器不在回归原处,而是始终为0,所以,CountDownLatch不可以重用。由此可见,门闩很形象的描述了它的工作模式。

2,Barrier

       CyclicBarrier,回环栅栏,它用于等待一组线程完成某个条件后再全部一起执行后续功能的能力,之所以称为回环,是因为与CountDownLatch一次性使用方式不同,它可以被反复使用,

       CyclicBarrier有多重使用方式,最基本的使用方式是使用计数器来约束线程。

       Barrier已经提供了和Latch一样的能力,那么CyclicBarrier的回归性质体现在哪里?稍加观察,可以注意到Generation对象,计数完成后,生成一个新的Generation对象,Generation对象的一致性保证了以下特性:Generation只有在计数器重置时重新生成。当trip.signalAll发生时,可能有超出计数器数量的线程被唤醒,Generation对象保持相同说明是当前计数过程中的线程,因此会再次阻塞,Generation对象不同的说明是上次计数过程的线程,不会再次阻塞,并且返回其计数值。这两个特性为CyclicBarrier实现了可重复利用的能力。

3,wait、notify

       在java语言中,有多种方法能够实现线程间的通信,其中很重要的就是Objec类中的wait和notify方法,因为这两个方法都是native方法,也就是说方法的具体实现是有虚拟机本地的C代码来实现的,因此这里就不给出详细的源代码解析了,wait与notify方法在synchronized块中被使用,也就是java文档中提到的非常重要的一点,wait/notify方法的调用必须处在该对象的锁monitor中,也就是说在调用这些方法时首先获得该对象的锁,否则抛出IllegalMonitorStateException异常。另外一个需要注意的是在PrepareEnvThrad线程调用完notify后,只有等待代码退出synchronized块后,WorkerThread才能获取到锁,需要注意的是,当调用wait方法后,线程会进入WAITING等待状态,后续被notify()后,并没有被立即执行,而是进入等待获取锁的阻塞队列。

4,ThreadLocal

       java.lang.ThreadLocal是自jdk1.2版本起提供的线程成员操作类。在多线程环境下开发工作中,会频繁遭遇一类使用场景,比如期望对同一个Runnable的执行,能够根据线程不同,而有所区分。在利用ThreadLocal实现的代码中,只要能访问到ThreadLocal变量的地方,都可以获取得到指定的值,但是如果仅仅是为了传递值,那么定义一个static变量不都可以做到吗?这里的终点在于,ThreadLocal#get获取到的值,对每一个线程是唯一的。

       ThreadLocalMap是一个弱引用集合,它的存值、取值实现类似于HashMap,使用了一个数组来存放数据,使用混淆压缩后的ThreadLocalHashCode作为数组下标。

       弱引用特性由父类WeakReference的Entry提供,如果熟悉java Collection,那么应当知道Entry一般用于Map的存值,WeakReference的特性在于,如果到了垃圾回收,那么弱引用对应实例将立刻变为不可达。

       使用弱引用的目的在于节约资源,ThreadLocal的使用范围被局限在单个线程内部,线程内的方法都是顺序执行的,如果在执行过程中执行了gc,那么可以认为该变量不会被再次使用。

       ThreadLocal.set会把指定值和当前线程绑定在一个Map里。

       get的源码和set的源码非常类似,也是通过获取当前现线程,然后获取指定的ThreadLocalMap内容。

       ThreadLocal.get会根据当前线程,找到map中绑定的值,如果map或者值尚未初始化,那么会调用setInitialValue进行初始化。

5,SuppledThreadLocal

       SuppledThreadLocal是ThreadLocal的子类,它的出现是为了动态产生初始化值,supplier是动态产生值的方法。SuppliedThreadLocal仅仅重写了initialValue方法,将其实现委托给了supplier。

原文地址:https://www.cnblogs.com/guanghe/p/13512660.html