日常学习记录Java 常用线程并发类的应用实例

Posted 盛夏温暖流年

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了日常学习记录Java 常用线程并发类的应用实例相关的知识,希望对你有一定的参考价值。

之前复习线程并发的时候曾经写过博客,对 ReentrantLock 的实现原理进行了详细的介绍,链接如下:

https://blog.csdn.net/j1231230/article/details/120572008

常用常新,趁着还没忘再回顾一下相关知识,保持学习的好习惯非常重要。

可以知道,ReentrantLock 实现的前提是 AbstractQueuedSynchronizer(抽象队列同步器),简称 AQS,是 java.util.concurrent 的核心,常用的线程并发类 CountDownLatch、CyclicBarrier、Semaphore 等都包括了一个继承自 AQS 抽象类的内部类。

本篇博客对这几个线程并发类 CountDownLatch、CyclicBarrier、Semaphore 的应用实例进行记录和介绍。


CountDownLatch

CountDownLatch(倒计时器):用来协调多个线程之间的同步。
比如在主线程中新建 n 个子线程,那 CountDownLatch 的参数需要设置的和线程数一致,当子线程都执行完成后,倒计时归零,此时等待的主线程才能继续执行。

代码应用实例

public class CountDownLatchDemo {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(2);
        new Thread() {
            @Override
            public void run() {
                try {
                    System.out.println("执行任务1");
                    Thread.sleep(1000);
                    System.out.println("执行任务1完毕");
                    countDownLatch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                try {
                    System.out.println("执行任务2");
                    Thread.sleep(1000);
                    System.out.println("执行任务2完毕");
                    countDownLatch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }.start();

        countDownLatch.await();
        // 两个都执行完毕才执行
        System.out.println("exit");
    }
}

运行结果如下所示:

我们在主线程中新建了 2 个子线程,这里的 CountDownLatch 的参数设置和子线程数保持一致,都是 2,当子线程都执行完成后倒计时归零,此时等待的主线程就能继续执行了,输出结果 “exit” 。


CyclicBarrier

CyclicBarrier(循环栅栏):它的功能比 CountDownLatch 更加复杂和强大,主要作用是让一组线程到达一个同步点时被阻塞,直到最后一个线程到达同步点时,屏障才会被解除。

举一个具体的例子,比如一个公司聚餐,每个员工都从家里出发,可以把每个人都看作是一个线程,每个线程调⽤ await() ⽅法告诉 CyclicBarrier 我已经到达了屏障,然后当前线程会被阻塞。等到最后一个员工到了(屏障解除),饭店才打开门让大家进去(线程才会开始工作)。

代码应用实例

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new Runnable() {
            @Override
            public void run() {
                System.out.println("全员到齐,开始吃饭!");
            }
        });

        ExecutorService es = Executors.newCachedThreadPool();
        try {
            for (int i = 0; i < 3; i++) {
                int user = i + 1;
                Runnable r = new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(1000);
                            System.out.println("用户" + user + "已到达");
                            cyclicBarrier.await();
                            System.out.println("我去吃饭了!");
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                };
                es.submit(r);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            es.shutdown();
        }
    }
}

运行结果如下所示:

分析可以得知,用户2的线程先运行,在原地阻塞等待;之后用户3的线程运行,也进入等待状态,最后用户1的线程运行,此时屏障得以解除,3 个线程才会正式开始工作。


Semaphore

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

代码应用实例

public class SemaphoreDemo {
    public static void main(String[] args) {
        int workers = 8;
        Semaphore semaphore = new Semaphore(5);
        for (int i=0; i<workers;i++){
            new Worker(i, semaphore).start();
        }
    }
}
class Worker extends Thread{
    private Integer num;
    private Semaphore semaphore;

    public Worker(Integer num, Semaphore semaphore){
        this.num = num;
        this.semaphore = semaphore;
    }

    @Override
    public void run(){
        try {
            semaphore.acquire();
            System.out.println("工人" + this.num + ",占用了一台机器");
            Thread.sleep(2000);
            System.out.println("工人" + this.num + ",释放了一台机器");
            semaphore.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果如下所示:

以上实例中,共有 8 个工人访问 5 台工作机器,也就是说 8 个线程中可以有 5 个同时进行操作,此时剩下的 3 个线程需要等待,等有线程完成操作,资源空闲下来后,其他线程才可以继续使用。

以上是关于日常学习记录Java 常用线程并发类的应用实例的主要内容,如果未能解决你的问题,请参考以下文章

Java并发学习

多线程高并发之Synchronized锁及其膨胀

[Java 并发编程实战] 设计线程安全的类的三个方式(含代码)

Java 并发:学习Thread 类

Java高并发学习笔记:Thread详解

并发编程专题-线程的创建方式