常用的并发编程工具CountDownLatch和CyclicBarrier介绍

线程门栓 CountDownLatch

介绍

从字面意思可以理解为类似一个门栓,能够使一个线程等待其余线程执行完后,此线程再继续执行。

特点

是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后之前等待的线程就可以恢复工作了。

常用的方法

/**
* 构造器
* @param count 计数次数
*/
public CountDownLatch(int count)
/**
* 阻塞等待,当计数不为0会一直等待
*/
public void await()
/**
* 阻塞等待
* @param timeout 等待的时间
* @param unit 时间单位
*/
public boolean await(long timeout, TimeUnit unit)
/**
* 将计数减1
*/
public void countDown()

举例1

对一个原子类的变量进行统计自增,主线程开启两个线程对这个变量自增加1,两个线程执行完后,主线程再对这个变量自增加1

public static void testCountDownLatch(){
    long startTime = System.currentTimeMillis();
    
    AtomicInteger count = new AtomicInteger(0);
    
    //设置countDownLatch要计数的次数
    CountDownLatch countDownLatch = new CountDownLatch(2);

    new Thread(() -> {
        try {
            Thread.sleep(1000);
            count.incrementAndGet();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }finally {
            //计数器减1
            countDownLatch.countDown();
        }
    }).start();
    new Thread(() -> {
        try {
            Thread.sleep(1000);
            count.incrementAndGet();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }finally {
            //计数器减1
            countDownLatch.countDown();
        }
    }).start();

    try {
        countDownLatch.await();
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }

    count.incrementAndGet();
    long endTime = System.currentTimeMillis();
    System.out.println("==次数:"+count.get()+",执行时间:"+(endTime - startTime)+"==");
}

结果:

==次数:3,执行时间:1037==

举例2

可以用线程安全的集合来接收线程返回的结果

public static void testCountDownLatch2(){
    long startTime = System.currentTimeMillis();
    List<String> list = Collections.synchronizedList(new ArrayList<String>());
    
    //设置countDownLatch要计数的次数
    CountDownLatch countDownLatch = new CountDownLatch(2);

    Thread thread1 = new Thread(() -> {
        try {
            Thread.sleep(1000);
            list.add(Thread.currentThread().getName());
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            //计数器减1
            countDownLatch.countDown();
        }
    });
    thread1.setName("thread-1");
    thread1.start();

    Thread thread2 = new Thread(() -> {
        try {
            Thread.sleep(1000);
            list.add(Thread.currentThread().getName());
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            //计数器减1
            countDownLatch.countDown();
        }
    });
    thread2.setName("thread-2");
    thread2.start();

    try {
        countDownLatch.await();
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }

    long endTime = System.currentTimeMillis();
    System.out.println("集合元素:");
    list.stream().forEach(System.out::println);
    System.out.println("执行时间:");
    System.out.println((endTime - startTime));
}

结果:

集合元素:
thread-2
thread-1
执行时间:
1042

注意

在调用countDown()方法时,finally代码块中调用,要保证计数器一定会成功的减1

执行过程

CyclicBarrier

介绍

从这个词的含义就能知道这是一个栅栏,可以指定让多个线程都在栅栏处等待住,直到到达栅栏的线程数到了指定的数量,再让通过,而且可以设置一个任务当都到达了栅栏时再运行。

常用方法

/**
* 构造器
* @param parties 要在栅栏处等待的线程数量
*/
public CyclicBarrier(int parties)
/**
* 构造器
* @param parties 要在栅栏处等待的线程数量
* @param barrierAction 设置的栅栏处等待的线程都通过后执行的任务
*/
public CyclicBarrier(int parties, Runnable barrierAction)
/**
* 调用此方法表示到达了栅栏在此等待
*/
public int await()
/**
* 调用此方法表示到达了栅栏在此等待
* @param timeout 在栅栏处等待的时间
* @param unit 在栅栏处等待的时间单位
*/
public int await(long timeout, TimeUnit unit)

举例

public class CyclicBarrierTest {

    public static String getThreadName(Integer i){
        return "thread-" + i;
    }
    public static void cyclicBarrierTest1(){

        ExecutorService executorService = 
                Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1);

        //设置要在栅栏等待数量为5个
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5,() -> {
            System.out.println("===所有的线程都冲过了栅栏===");
        });

        List<Runnable> list = new ArrayList<>();
        for (int i = 1 ; i <= 5 ; i++) {
            int temporaryVariate = i;
            list.add(() -> {
                try{
                    System.out.println("==="+getThreadName(temporaryVariate)+"在栅栏等待====");
                    cyclicBarrier.await();
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println("==="+getThreadName(temporaryVariate)+"冲过了栅栏====");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            });
        }
        for (Runnable runnable : list) {
            executorService.execute(runnable);
        }
    }

    public static void main(String[] args) {
        cyclicBarrierTest1();
    }
}

结果:

===thread-2在栅栏等待====
===thread-5在栅栏等待====
===thread-4在栅栏等待====
===thread-3在栅栏等待====
===thread-1在栅栏等待====
=====所有的线程都冲过了栅栏=====
===thread-2冲过了栅栏====
===thread-5冲过了栅栏====
===thread-1冲过了栅栏====
===thread-3冲过了栅栏====
===thread-4冲过了栅栏====

Semaphore

介绍

从字面意思可能理解是一个信号量,作用是能够控制执行的线程数。

特点

首先设置好信号量的数量,当拿到了信号量的线程就可以运行,信号量就减1。执行完再把信号量归还回去,信号量就加1。而没有获得到信号量的线程就要等待其他的线程归还后,拿到了信号量才可以运行。

常用的方法

/**
* 构造器
* @param permits 信号量的数量
*/
public Semaphore(int permits)
/**
* 构造器
* @param permits 信号量的数量
* @param fair 是否支持公平性
*/
public Semaphore(int permits, boolean fair)
/**
* 获取信号量
*/
public void acquire()
/**
* 获取信号量
* @param permits 获取信号量的数量
*/
public void acquire(int permits)
/**
* 尝试获取信号量
*/
public boolean tryAcquire()
/**
* 尝试获取信号量
* @param timeout 尝试获取的时间
* @param unit 时间单位
*/
public boolean tryAcquire(long timeout, TimeUnit unit)
/**
* 释放信号量
*/
public void release()

举例

public class SemaphoreTest {
    public static String getThreadName(Integer i){
        return "thread-" + i;
    }
    public static void SemaphoreTest(){
        ExecutorService executorService = Executors
                  .newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1);
        //设置信号量为5个          
        Semaphore semaphore = new Semaphore(5);

        List<Runnable> list = new ArrayList<>();
        for (int i = 1 ; i <= 10 ; i++) {
            int temporaryVariate = i;
            list.add(() -> {
                try{
                    semaphore.acquire();
                    System.out.println("==="+getThreadName(temporaryVariate)+"拿到了信号量====");
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally{
                    System.out.println("==="+getThreadName(temporaryVariate)+"归还了信号量====");
                    semaphore.release();
                }
            });
        }
        for (Runnable runnable : list) {
            executorService.execute(runnable);
        }
    }
    public static void main(String[] args) {
        SemaphoreTest();
    }
}

结果:

===thread-3拿到了信号量====
===thread-5拿到了信号量====
===thread-4拿到了信号量====
===thread-2拿到了信号量====
===thread-1拿到了信号量====
===thread-5归还了信号量====
===thread-4归还了信号量====
===thread-2归还了信号量====
===thread-3归还了信号量====
===thread-1归还了信号量====
===thread-9拿到了信号量====
===thread-10拿到了信号量====
===thread-8拿到了信号量====
===thread-7拿到了信号量====
===thread-6拿到了信号量====
===thread-7归还了信号量====
===thread-6归还了信号量====
===thread-8归还了信号量====
===thread-9归还了信号量====
===thread-10归还了信号量====
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值