Day299.控制并发流程 -Juc

Posted 阿昌喜欢吃黄桃

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Day299.控制并发流程 -Juc相关的知识,希望对你有一定的参考价值。

控制并发流程

一、什么是控制并发流程?

  • 作用

帮助程序员更容易得让线程之间相互配合,来满足业务逻辑;让各个线程之间动作协调

  • 比如

让线程A等待线程B执行完后再执行等待合作策略;

或一系列线程等待一个线程运行完毕或发号等…

二、控制并发流程工具类

image-20210615203221302

三、CountDownLatch倒计时门栓

倒数count为0后,执行await()方法阻塞的线程就发车

1、类作用

过山车,人满了指挥员一声令下发车

  • 流程

倒数结束之前,一直处于等待状态,直到倒计时结束。此线程才继续工作。

image-20210615203630919

2、主要方法

image-20210615203907930


3、图解

在构造方法中指定倒数count值;

调用await的线程会被挂起;

每调用countDown(),倒数count会减1;但是不会被挂起,依然执行

当倒数count值为0时,被await的线程就被释放,开始执行

image-20210615204512312


4、代码演示

  • 用法一1等多

可以多线程同时执行

一个线程等待多个线程都执行完毕,再继续自己的工作

/******
 @author 阿昌
 @create 2021-06-15 20:48
 *******
 *      工厂中,质检,5个工人检查,当5个人都认为通过,才认为这个质检通过
 */
public class CountDownLatchDemo1 {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(5);//指定倒数count为5
        ExecutorService pool = Executors.newFixedThreadPool(5);//线程池创建5个线程

        //5次任务质检
        for (int i = 0; i < 5; i++) {
            int no = i+1;
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep((long) (Math.random() * 10000));
                        System.out.println("No." + no + ":质检完毕");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        countDownLatch.countDown();//倒计count减1
                    }
                }
            };

            pool.submit(runnable);
        }
        System.out.println("等待5个人质检完。。。。。");
        countDownLatch.await();//主线程等待倒时count=0,释放所有挂起线程,并主线进行工作
        System.out.println("质检完成");

    }
    
}

image-20210615205913966


  • 用法二多等1

多个线程等待某个线程的信号,同时开始执行

/******
 @author 阿昌
 @create 2021-06-15 20:48
 *******
 *      模拟100m跑步,5名选手都准备好了,只能裁判员一生令下,5人同时跑出
 */
public class CountDownLatchDemo2 {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        ExecutorService pool = Executors.newFixedThreadPool(5);//线程池创建5个线程

        for (int i = 0; i < 5; i++) {
            int no = i+1;
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    System.out.println("NO." + no + ":准备完毕,等待发令");
                    try {
                        countDownLatch.await();
                        System.out.println("No." + no + ":开始跑步");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            pool.submit(runnable);
        }
        //主线程,模拟裁判员
        Thread.sleep(5000);
        System.out.println("发令枪响,比赛开始");
        countDownLatch.countDown();
    }

}

image-20210615211529783


  • 用法三:综合用法,1等多&多等1

1等多:裁判员等5名运动员到达终点

多等1:5名运动员等待裁判员打枪开跑

/******
 @author 阿昌
 @create 2021-06-15 20:48
 *******
 *      模拟100m跑步,5名选手都准备好了,只能裁判员一生令下,5人同时跑出,当所有人都到终点后,比赛结束
 */
public class CountDownLatchDemo3 {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch begin = new CountDownLatch(1);
        CountDownLatch end = new CountDownLatch(5);
        ExecutorService pool = Executors.newFixedThreadPool(5);//线程池创建5个线程

        for (int i = 0; i < 5; i++) {
            int no = i+1;
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    System.out.println("NO." + no + ":准备完毕,等待发令");
                    try {
                        begin.await();
                        System.out.println("No." + no + ":开始跑步");
                        Thread.sleep((long) (Math.random()*10000));
                        System.out.println("No." + no + ":到达终点");
                        end.countDown();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        end.countDown();
                    }
                }
            };
            pool.submit(runnable);
        }
        //主线程,模拟裁判员
        Thread.sleep(5000);
        System.out.println("发令枪响,比赛开始");
        begin.countDown();
        end.await();
        System.out.println("比赛结束!!!");
    }

}

image-20210615211917148


5、注意点

  • 他不能重用,当倒数count=0,就失效了;
  • 如果要再次使用,再实例化新的对象
  • 可以实现多等多的情况

image-20210615212530751


6、总结

image-20210615212622501

四、Semaphore信号量

1、类作用

用于一些重量级服务,如执行时间长、处理消耗资源大;来设置同时并发执行任务线程个数,只能有拿到许可证的线程才可以执行

  • 用来限制或管理数量的有限资源的使用情况

  • 类比生活中的:"许可证"

  • 这个许可证的数量有限

image-20210615212950415


2、图解

image-20210615213115390

image-20210615213748027


3、使用流程

image-20210615213841983


4、主要方法

image-20210615214100497

image-20210615214029740

image-20210615214200658


5、代码演示

  • 一般用法
public class SemaphoreDemo {
    static Semaphore semaphore = new Semaphore(3,true);
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(50);

        for (int i = 0; i < 100; i++) {
            pool.submit(new Task());
        }
        pool.shutdown();

    }
    static class Task implements Runnable{
        @Override
        public void run() {
            try {
                semaphore.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":拿到许可证");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":释放的许可证");
            semaphore.release();
        }
    }
}

image-20210615214857350


  • 特殊用法

image-20210615215029815

image-20210615215035537

image-20210615215212686


6、注意点

  • 获取&释放的数量要求必须一致,否则程序会卡死

  • 根据情况设置公平性,一般设置为true

  • 释放和获取对线程没有要求,可以由这个线程获取,别的线程释放

  • 可以将它实现成一个轻量级的CountDownLatch

image-20210615215528083

image-20210615215655031

五、Condition条件对象

1、类作用

image-20210615215837738


2、图解

image-20210615215917491


3、signalAll()和signal()的区别

  • signalAll()唤醒所有等待的线程
  • signal()是公平的,唤醒等待时间最长的线程

4、代码演示

condition一般与lock进行搭配使用

  • 普通用法
public class ConditionDemo1 {
    private ReentrantLock lock = new ReentrantLock();
    private Condition condtion = lock.newCondition();

    void method1() throws InterruptedException {
        lock.lock();
        try{
            System.out.println("条件不满足,开始wait");
            condtion.await();
            System.out.println("条件满足,开始执行后续的任务");
        }finally {
            lock.unlock();
        }
    }

    void method2(){
        lock.lock();
        try{
            System.out.println("准备工作完成,开始唤醒其他线程");
            condtion.signal();
        }finally {
            lock.unlock();
        }
    }

    //主函数
    public static void main(String[] args) throws InterruptedException {
        ConditionDemo1 demo1 = new ConditionDemo1();
        //主线程创建一个线程1
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                    demo1.method2();//线程1,1秒后,唤醒主线程
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        demo1.method1();//主线程阻塞
    }
    
}

image-20210615220926706


  • 用Condition实现生产者消费者模式
/******
 @author 阿昌
 @create 2021-06-15 22:01
 *******
 *      演示Condition实现生产者消费者模式
 */
public class ConditionDemo2 {
    private static int queueSize = 10;
    private static PriorityQueue<Integer> queue = new PriorityQueue<>(queueSize);

    private static ReentrantLock lock = new ReentrantLock();
    private static Condition noFull = lock.newCondition();
    private static Condition notEmpty = lock.newCondition();

    //消费者
    static class Consumer extends Thread {
        @Override
        public void run() {
            comsume();
        }

        //消费操作
        void comsume(){
            while (true){
                lock.lock();
                try {
                    while (queue.size()==0){
                        System.out.println("队列空,等待数据");
                        notEmpty.await();
                    }
                    queue.poll();
                    noFull.signalAll();
                    System.out.println("从队列里取走了一个数据,队列还剩余空间"+(queueSize-queue.size())+"个元素");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //生产者
    static class Producer extends Thread {
        @Override
        public void run() {
            produce();
        }

        //生产操作
        void produce(){
            while (true){
                lock.lock();
                try {
                    while (queue.size()==queueSize){
                        System.out.println("队列满,等待消费");
                        noFull.await();
                    }
                    queue.offer(1);
                    notEmpty.signalAll();
                    System.out.println("给队列生成了一个数据,队列有"+queue.size()+"个元素");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //主函数
    public static void main(String[] args) {
        Consumer consumer = new Consumer();
        Producer producer = new Producer();
        consumer.start();
        producer.start();
    }

}

image-20210615223633450


5、注意点

await会自动释放锁,无需手动释放

image-20210615223826718


六、CyclicBarrier循环栅栏

满足数量条件,就发车

1、类作用

image-20210615224141342


2、代码演示

它是可以重用的!!!!!!!!

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        //参数1:设置几个等待数
        //参数2:当线程数满足条件后,执行的任务
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() {
            @Override
            public void run() {
                System.out.println("这一波人到齐了,出发");
            }
        });

        //创建10个线程
        for (int i = 0; i < 10; i++) {
            new Thread(new Task(i,cyclicBarrier)).start();
        }

    }

    static class Task implements Runnable{
        private int id;
        private CyclicBarrier cyclicBarrierDay1[下] - Python基础  基本语法流程控制

Day1 - Python基础1 介绍基本语法流程控制

python全栈开发-Day2 布尔流程控制循环

day1 python 介绍基本语法流程控制

Java从小白到入门,Day3(程序流程控制)

Python开发基础-Day2-流程控制数字和字符串处理