Java并发编程知识点总结(二十二)——各种并发工具类详解

1. CountDownLatch

CountDownLatch其实就是一个计数器,应用场景主要是在线程之间的等待上面。
举个简单的例子:假设学校规定必须全部的学生到齐,老师才能上课。老师首先来到教室,这时班上的同学都还没有来,老师设置计数器为50,进行逐一倒数,并执行等待方法await()。然后同学们相继到达教室,计数器的值也就一直在递减。最后,全班同学都到达了,那么计数器也就减为0了,这时老师就无需等待了,可以继续执行任务。
CountDownLatch的方法并不多,只有如下几个方法:

方法功能
CountDownLatch(int count)构造方法,设置计数器的初始值
await()等待方法,等待计数器减为0
await(long timeout, TimeUnit unit)限时等待,如果超过一定时间就不再等待
countDown()计数器数目-1
getCount()获得计数器的数值
下面举出一个具体的实例:
public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(10);
        Thread thread = new Thread(){
            @Override
            public void run() {
                for(int i = 0; i < 10; i++) {
                    System.out.println("学生" + (i+1) + "到达教室");
                    countDownLatch.countDown();
                }
            }
        };

        System.out.println("老师到达教室");
        thread.start();
        try {
            countDownLatch.await();
            System.out.println("学生到齐,开始上课");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

运行结果:

老师到达教室
学生1到达教室
学生2到达教室
学生3到达教室
学生4到达教室
学生5到达教室
学生6到达教室
学生7到达教室
学生8到达教室
学生9到达教室
学生10到达教室
学生到齐,开始上课

2. CyclicBarrier

CyclicBarrier可以理解为一个循环栅栏,其实和CountDownLatch有一定的相同点。就是都是需要进行计数,CyclicBarrier是等待所有人都准备就绪了,才会进行下一步。不同点在于,CyclicBarrier结束了一次计数之后会自动开始下一次计数。而CountDownLatch只能完成一次。
下面来举个简单的例子: 例如一个军队上战场打仗,出征前需要集合报数。假设总共有10个士兵,依次报数,当10个士兵都报完数了,才能出征。
下面是CyclicBarrier中的一些主要方法:

方法功能
await()等待方法
await(long timeout, TimeUnit unit)限时等待方法
isBroken()查看等待的线程是否被中断
reset()重置栅栏,如果有线程在等待会抛出BrokenBarrierException
getNumberWaiting()查看当前有多少个线程在等待

下面是一个简单的例子进行运用:

import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestCyclicBarrier {
    public static void main(String[] args) {
        // 这里利用了CyclicBarrier的另外一个构造函数传入Runnable接口
        final CyclicBarrier cyclicBarrier = new CyclicBarrier(10, new Runnable() {
            @Override
            public void run() {
                // 当等待成功后会执行
                System.out.println("教官:全员出发");
            }
        });
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        System.out.println("教官:开始报数");
        Thread thread = new Thread() {
            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName() + "士兵就位");
                    cyclicBarrier.await();
                    System.out.println(Thread.currentThread().getName() + "号士兵出发");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        for (int i = 0; i < 10; i++) {
            executorService.execute(thread);
        }
    }
}

运行结果:

教官:开始报数
pool-1-thread-2士兵就位
pool-1-thread-3士兵就位
pool-1-thread-1士兵就位
pool-1-thread-5士兵就位
pool-1-thread-4士兵就位
pool-1-thread-6士兵就位
pool-1-thread-8士兵就位
pool-1-thread-7士兵就位
pool-1-thread-9士兵就位
pool-1-thread-10士兵就位
教官:全员出发
pool-1-thread-10号士兵出发
pool-1-thread-2号士兵出发
pool-1-thread-1号士兵出发
pool-1-thread-3号士兵出发
pool-1-thread-9号士兵出发
pool-1-thread-7号士兵出发
pool-1-thread-8号士兵出发
pool-1-thread-6号士兵出发
pool-1-thread-4号士兵出发
pool-1-thread-5号士兵出发

从上面的代码中可以看到,只有全员到齐了,才会出发。

接下来,我们对比下CountDownLatch和CyclicBarrier的区别

  1. CountDownLatch通常用于一个线程等多个线程。CyclicBarrier则是多个线程互相等待,然后一起执行。
  2. CountDownLatch的countDown方法执行后,不会阻塞,会继续执行。CyclicBarrier的await方法执行后,会被阻塞。
  3. CountDownLatch不能复用,CyclicBarrier能够复用。

3. Semaphore

Semaphore主要是用来控制资源数量的工具,可以理解为信号量。初始化时给定信号量的个数,其他线程可以来尝试获得许可,当完成任务之后需要释放响应的许可。
同样我们来举个例子: 我们去银行办理业务通常都需要填表,而银行通常会放置几支笔。当时办理业务的人数肯定是多于笔的总数的。我们就可以这样理解,这几只笔就相当于信号量,排在前面的人可以先获得许可,用完笔之后,就需要把笔还回去,相当于释放许可。

方法功能
Semaphore(int permits)构造方法,给定许可证的个数
acquire()尝试获得一个许可
acquire(int permits)尝试获得多个许可
release()尝试释放许可
release(int permits)尝试释放多个许可

下面展示一个简单的例子:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class TestSemaphore {
    public static void main(String[] args) {
        final Semaphore semaphore = new Semaphore(10);
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        Thread thread = new Thread() {
            @Override
            public void run() {
                try {
                    semaphore.acquire(1);
                    System.out.println(Thread.currentThread().getName() + "拿到笔" + System.currentTimeMillis());
                    Thread.sleep(1000);
                    semaphore.release(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        for (int i = 0; i < 10; i++) {
            executorService.execute((thread));
        }
    }
}

输出结果:

pool-1-thread-1拿到笔1670901372706
pool-1-thread-5拿到笔1670901372706
pool-1-thread-2拿到笔1670901372706
pool-1-thread-4拿到笔1670901372706
pool-1-thread-3拿到笔1670901372706
pool-1-thread-1拿到笔1670901373712
pool-1-thread-4拿到笔1670901373712
pool-1-thread-5拿到笔1670901373712
pool-1-thread-2拿到笔1670901373712
pool-1-thread-3拿到笔1670901373712

从结果中可以看到,在同一时刻只有5个线程能拿到笔。

4.Exchanger

Exchanger是一个用于线程间协作的工具类。它提供了一个交换的同步点,在这个交换点两个线程能够交换数据。具体交换数据的方式是通过exchange函数实现的,如果一个线程先执行exchange函数,那么它会等待另一个线程也执行exchange方法。当两个线程都到达了同步交换点,两个线程就可以交换数据。

同样我们来举个例子: 在战争中,长官会派士兵去前方侦查,长官则会留在司令部等待消息。所以长官会先执行exchange方法进行等待,当士兵侦查到消息,返回到司令部了,也会执行exchage方法。这样,长官和士兵就可以交换消息了。

方法功能
exchange(V x)将数据交换给另一个线程,同时返回获取的数据
exchange(V x, long timeout, Timeout unit)和上一个方法一样,只是增加了超时等待功能

下面展示一个简单的例子:

import java.util.concurrent.Exchanger;

public class TestExchanger {
    public static void main(String[] args) {
        final Exchanger<String> exchanger = new Exchanger<String>();
        Thread chief = new Thread() {
            @Override
            public void run() {
                try {
                    String message = exchanger.exchange("长官:收到:就地隐蔽!");
                    System.out.println(message);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };

        Thread soldier = new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(500);
                    String message = exchanger.exchange("士兵:报告长官:前方有敌情!");
                    System.out.println(message);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        soldier.start();
        chief.start();
    }
}

输出结果:

士兵:报告长官:前方有敌情!
长官:收到:就地隐蔽!

参考文章:
大白话说java并发工具类-CountDownLatch,CyclicBarrier
大白话说java并发工具类-Semaphore,Exchanger

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值