并发编程(十五):Java并发工具类

目录


学习资料

《Java并发编程的艺术》第8章


1.CountDownLatch

CountDownLatch允许一个或多个线程等待其他线程完成操作

thread.join()可让当前线程等待thread执行完毕再执行,原理是不停检查join的线程是否存活,如果thread线程存活则让当前线程永远等待

CountDownLatch可以实现join的功能且比join的功能更强大

@Test
public void test01() throws InterruptedException {
    CountDownLatch count=new CountDownLatch(2); //设置计数器为2
    
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println(1);
            count.countDown();	//计数器-1
            System.out.println(2);
            count.countDown();	//计数器再-1
        }
    }).start();
    
    count.await();	//计数器不为0,当前线程会一直等待
    System.out.println(3);
}
  • 调用countDown()方法时,计数器会减一,CountDownLatch的await()方法会阻塞当前线程(调用线程)直到计数器变为0
  • 可以在多个线程调用countDown(),只需要将CountDownLatch引用传递给其他线程即可
  • 计数器无法重置
  • 可以使用await(long time,TimeUnit unit)指定一个超时时间

2.同步屏障CyclicBarrier

2.1 简介

CyclicBarrier,可循环使用的屏障,让一组线程到达一个屏障时(同步点)被阻塞,直到最后一个线程到达屏障时,所有被屏障拦截的线程才会继续运行

有两个常用构造方法:

  • CyclicBarrier(int parties):表示拦截parties个线程之后才会"开门"
  • CyclicBarrier(int parties,Runnable barrierAction):在拦截之前调用barrierAction

示例代码:(用第二个构造函数)

//CyclicBarrier
@Test
public void test02() throws BrokenBarrierException, InterruptedException {

    //数量改为3永远不执行,因为没有第三个线程等待
    CyclicBarrier c=new CyclicBarrier(2, new Runnable() {
        @Override
        public void run() {
            //barrierAction
            System.out.println("先执行");
        }
    });
    
    //第一个线程
    new Thread(new Runnable() {
        @SneakyThrows
        @Override
        public void run() {
            c.await();  //第一个阻塞
            System.out.println("第一个线程:thread");
        }
    }).start();
    
    c.await();  //第二个阻塞,开门
    System.out.println("第二个线程:main");
}

2.2 应用场景

CyclicBarrier可用于多线程计算,最后合并每个线程的计算结果

2.3 CyclicBarrier和CountDownLatch的区别

CountDownLatch的计数器只能使用一次,CyclicBarrier计数器可以使用reset()方法重置,如果出错可以重置并重新执行线程

CyclicBarrier的其他有用方法:

  • getNumberWaiting():获取阻塞的线程数量
  • isBroken():了解阻塞线程是否被中断过(可在异常中获取)

3.Semaphore

Semaphore(信号量),用来控制访问特定资源的线程数量,通过协调各个线程,以保证合理的使用公共资源

3.1 应用场景

Semaphore可用于做流量控制,特别是公用资源有限的应用场景,比如数据库连接

如读取文件线程池启动了30个线程,然后保存数据库,数据库连接只有10个,线程不能超过10个,可以用Semaphore来限流:

@Test
public void test03(){
    ExecutorService threadpool= Executors.newFixedThreadPool(30);
    Semaphore s=new Semaphore(10);  //限流
    
    //循环30次
    for(int i=0;i<30;i++){
        threadpool.execute(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                s.acquire();    //获取许可证
                System.out.println("连接数据库"); //同时只能10个连接,最终会打印出30个
                s.release();    //使用完之后,归还许可证
            }
        });
    }
    
    threadpool.shutdown();
}

3.2 常用方法

  • acquire():获取许可证
  • release():归还许可证
  • avalilablePermits():返回此信号量中当前可用许可证数
  • getQueueLength():返回正在等待获取许可证的线程数
  • hasQueuedThreads():是否有线程正在等待获取许可证
  • reducePermits(int reduction):减少reduction个许可证
  • Collection getQueuedThreads():返回等待获取许可证的线程集合

4.Exchanger

Exchanger<E>是一个用于线程间协作的工具类。用于进行线程间数据交换

第一个线程执行exchange(msg1)方法,会一直等待第二个线程也执行exchange(msg2)方法,当两个线程到大同步点时,两个线程就可以交换数据(msg1给第二个线程,msg2给第一个线程)

示例代码:

Exchanger<String> exgr=new Exchanger<String>();
...
//第一个线程内
String bStr=exgr.exchange("msg1");	//a传送数据给b线程,接收b线程的数据
...
//第二个线程内
String aStr=exgr.exchange("msg2");	//数据传给a线程,接收a线程数据
...

可以使用exchange(V x,long timeout,TimeUnit unit)设置最大等待时长


posted @ 2021-03-11 21:19  菜鸟kenshine  阅读(88)  评论(0)    收藏  举报