Java并发编程艺术 8 Java中的并发工具类

来源:互联网 发布:csgo网络不稳定 编辑:程序博客网 时间:2024/06/05 16:12
第8章 Java中的并发工具类

并发工具类中主要提供了一些并发流程控制的手段。主要有CountDownLatch、CyclicBarriery和Semaphore
Exchanger工具类提供在线程间交换数据的一种手段。


CountDownLatch 
CountDownLatch类能够使一个线程进行等待,等待其他相关的线程完成各自的任务以后再执行当前线程的工作。
例如:一个程序的主线程希望负责启动框架的所有主线程完成以后再继续执行。

CountDownLatch是通过一个计数器来实现的,计数器的初始值就是相关线程的数量。当每一个相关线程完成任务的时候,对计数器的值减1。当计数器的值到达0时,他表示所有的相关线程已经完成了任务。然后使用CountDownLatch闭锁上等待的线程可以继续执行任务。(建议将countDown()放在finally里运行)
1.主线程线程开始
2.在主线程中创建一个CountDownLatch,根据N个相关线程。CountDownLatch初始值为N。
3.主线程使用await,在CountDownLatch上等待
4.相关线程完成任务后,对CountDownLatch减1.  countDown()。
5.当CountDownLatch为0时,主线程继续运行。
CountDownLatch的三个基本元素
CountDownLatch latch = new CountDownLatch(5); //主线程
latch.await()
latch.countDown() //子线程

CountDownLatch对象内部计数器的值只能被初始化一次,必能重新初始化。一旦赋值以后,以为能修改计数的方法就是countDown()方法,对内部计数器减1。 当计数器到达0时, 全部调用 await() 方法会立刻返回,接下来任何countDown() 方法的调用都将不会造成任何影响。
总结:
CountDownLatch是同步工具类中的一种。可以使一个线程进行等待,直到其他线程执行完毕后再执行当前线程。是通过内部计数器来实现的,每完成一个任务就对计数器进行减1。直到为0,闭锁等待的线程就可以恢复执行。可以用来实现线程等待和最大并行性。

CyclicBarriery
CyclicBarriery的默认构造方法CyclicBarrier(int parties),初始化参数表示屏障拦截的线程数。
当每一个线程调用await的时候通知CyclicBarrier已经到达屏障,然后当前线程被阻塞在await()方法上。
等到拦截到初始化时设定的线程数时,所有线程继续执行,之后再执行await()不会再等待。如果没有拦截到指定数量的线程,这全部都阻塞。

CyclicBarriery还提供了更高级的构造,可以传入barrierAction(Runnable类型),当所有线程到达屏障时,优先执行barrierAction。可以用作所有屏障到达的一个通知点
static CyclicBarrier b = new CyclicBarrier(10);

static CyclicBarrier b = new CyclicBarrier(10,new Runnable() {
     public void run() {
           System.out.println("hahhha");
     }
});

public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
     for(int i=0;i<10;i++){
           new Thread(new MRunnable()).start();
     }
     b.await();
     System.out.println("main");
     b.reset();
     b.await();
     System.out.println("main");
     
}
static class MRunnable implements Runnable{
     public void run() {
           try {
                Thread.sleep(5000);
                System.out.println("MRunnanle1 ");
                b.await();
           } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
           }
     }
}

实现最大的并行性:有时我们想同时启动多个线程,实现最大程度的并行性。
开始执行前等待n个线程完成各自任务:例如应用程序启动类要确保在处理用户请求前,所有N个外部系统已经启动和运行了。
通过将await()放在任务前还是任务后,来实现以上两种功能。

CountDownLatch 和CyclicBarrier区别
【1】.CountDownLatch计数器只能使用一次,CyclicBarrier的计数器可以通过reset()重置。
【2】CyclicBarrier中还提供了getNumberWaiting()方法获取阻塞的线程数。,以及一些其他方法。
【3】CountDownLatch使用减计数方式,从指定值开始减,减到为0释放所有线程。CyclicBarrier使用加计数方式,从0开始加到指定值释放所有线程
【4】CountDownLatch 的await()只负责阻塞,使用CountDown()方法减一。CyclicBarrier的await(),负责阻塞,并且对计数加一。

Semaphore
Semaphore(信号量)用于控制同时访问特定资源的线程数量,可以协调各个线程保证合理的使用公共资源。
比如:访问共享文件,只允许最多十个线程同时访问。比如数据库连接,最多支持十个线程连接。
static Semaphore s = new Semaphore(10);

public static void main(String[] args) throws InterruptedException {
     for(int i=0;i<30;i++){
           new Thread(new MyRunnabe()).start();
     }
     Thread.sleep(2000);
     System.out.println(s.availablePermits());
     System.out.println(s.getQueueLength());
     while(true){
           if (s.getQueueLength()==0) {
                break;
           }
           System.out.println("in waiting thread : "+s.getQueueLength());
           Thread.sleep(5000);
     }
}
static class MyRunnabe implements Runnable{
     public void run() {
           try {
                s.acquire();
                System.out.println("out");
                Thread.sleep(10000);
                s.release();
           } catch (InterruptedException e) {
                e.printStackTrace();
           }
     }

}

int availablePermits()  放回信号量中可用的许可证数量
int getQueueLength()    返回正在等待获取许可证的线程数
boolean  hasQueuedThread() 当前是否有线程正在等待许可证
void  reducePermits()  减少许可证数量,是protected方法
Collection getQueuedThreads()  返回所有等待的获取许可证的线程集合,是protected方法。


Exchanger
线程间交换数据。Exchanger(交换者)是线程间协作的工具类。用于线程之间的数据交换。
提供一个同步点,实现两个线程之间的数据交换。当一个线程执行了exchange方法,会等待另个线程执行exchange方法。当两个线程同时到达同步点就可以交换数据,将本线程的数据传递给对方。