一文熟悉Java并发通信三剑客

2021/10/13 1:14:14

本文主要是介绍一文熟悉Java并发通信三剑客,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

在java并发中,CountDownLatch、CycliBarrier、Semaphore是很重要的同步工具类,都位于java.util.concurrent包下,下面分别介绍其用法和区别。

1.CountDownLatch

在JUC中,可以使用 Countdownlatch实现闭锁。其原理是, Countdownlatch在创建时,会指定一个计数器,表示等待线程的执行数量(例如,5就表示当5个线程执行完毕后,再闭锁,使A能够继续执行)。之后,其他每个线程在各自执行完毕时,分别调用一次 countdown方法,用来递减计数器,表示有一个线程已经执行完毕了。与此同时,线程A可以调用 await0方法,用来等待计数器的值为0。如果计数器的值大于0,那么 await方法会一直阻塞;直到计数器为0时线程A才会继续执行。如果线程A一直无法等到计数器为0,则会显示等待超时;当然也可以在线程A等待时,通过程序中断等待。举个生活中的例子就好比上晚自习,规定班长在同学全部离开后,班长最后关灯锁门。这个班长就是线程A。Demo如下:

​
import java.util.concurrent.CountDownLatch;
​
public class TestCountDownLatch {
​
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(10);
        MyThread myThread = new MyThread(countDownLatch);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(myThread, "T" + i);
            thread.start();
        }
        System.out.println("========");
        countDownLatch.await();
        long end = System.currentTimeMillis();
        System.out.println("耗时:" + (end - start));
    }
​
    static class MyThread implements Runnable {
​
        private CountDownLatch countDownLatch;
​
        public MyThread(CountDownLatch countDownLatch) {
            this.countDownLatch = countDownLatch;
        }
​
        @Override
        public void run() {
            try {
                Thread.sleep(300);
                System.out.println(Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                countDownLatch.countDown();
            }
        }
    }
}

​

2. CyclicBarrier

与 Countdownlatch类似, Cyclicbarrier也可以用于解决多个线程之间的相互等待问题。Cyclic Barrier的使用场景是,每个线程在执行时,都会碰到屏障,该屏障会拦截所有线程的执行(通过 await方法实现);当指定数量的线程全部就位时,所有的线程再跨过屏障同时执行。现举例说明 Countdownlatch和 Cyclicbarrier的区别。假设有A、B、C3个线程,其中C是最后一个加入的线程。举个开会的例子,只有等到最后一个人到了才能开始会议。Demo如下:

import java.io.IOException;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
​
public class TestCyclicBarrier {
​
    static class MyThread implements Runnable {
        //用于控制会议开始的屏障
        private CyclicBarrier barrier;
        //参会人员
        private String person;
​
        public MyThread(CyclicBarrier barrier, String name) {
            this.barrier = barrier;
            this.person = name;
        }
​
        @Override
        public void run() {
            try {
                Thread.sleep((int) (10000 * Math.random()));
                System.out.println(person + " 已经到会...");
                barrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println(person + " 开始会议...");
        }
    }
​
    public static void main(String[] args) throws IOException, InterruptedException {
        //将屏障设置为3,即当有3个线程执行到await()时,再同时释放
        CyclicBarrier barrier = new CyclicBarrier(3);
        ExecutorService executor = Executors.newFixedThreadPool(3);
        //三个人去开会
        executor.submit(new MyThread(barrier, "zs"));
        executor.submit(new MyThread(barrier, "ls"));
        executor.submit(new MyThread(barrier, "ww"));
        executor.shutdown();
    }
}

3.Semaphor

Semaphore称为信号量,是引自操作系统中的概念。在Java中, Semaphore可以通过 permits属性控制线程的并发数。在使用了 Semaphore的编程中,默认情况下所有线程都处于阻塞状态。可以用 Semaphore的构造方法设置可执行线程的并发数(即 permits的值,如5),然后通过 acquire方法允许同一时间只能有5个线程同时执行;并且在这5个线程中,如果某个线程执行完毕,就可以调用 release释放一次执行的机会,然后从其他等待的线程中随机选取一个来执行。同一时间只允许3个线程执行,但是有10个线程尝试并发执行,Demo如下:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
​
public class TestSemaphore {
​
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        // 同一时间,只允许3个线程并发访问
        Semaphore semp = new Semaphore(3);
        // 创建10个线程
        for (int i = 0; i < 10; i++) {
            final int threadNo = i;
            //execute()方法的参数:重写了run()方法的Runnable对象
            executor.execute(() -> {
                  try {
                      //同一时间,只能有3个线程获取许可去执行
                      semp.acquire();
                      System.out.println("得到许可并执行的线程: " + threadNo);
                      Thread.sleep((long) (Math.random() * 10000));
                      // 得到许可的线程执行完毕后,将许可转让给其他线程
                      semp.release();
                  } catch (InterruptedException e) {
                  }
              }
            );
        }
        executor.shutdown();
    }
​
}

总结:

Countdownlatch可以实现当A和B全部执行完毕后,C再去执行。Cyclicbarrier可以实现A、B等到C就绪后( await(0表示就绪),A、B、C三者再同时去执行。Countdownlatch是不能够重用的,而Cyclicbarrier是可以重用的。Semaphore其实和锁有点类似,它一般用于控制对某组资源的访问权限。



这篇关于一文熟悉Java并发通信三剑客的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程