多线程学习笔记-4.多线程并发控制流程

@TOC

1. 什么是控制并发流程


在这里插入图片描述

2. CountDownLatch

在这里插入图片描述

2.1 类的作用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2 两个经典用法

1. 一个线程等待多个线程
  • 一个线程等待多个线程都在执行完毕,再继续自己的工作
/**
 * 描述:     工厂中,质检,5个工人检查,所有人都认为通过,才通过
 */
public class CountDownLatchDemo1 {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(5);// 5个等待线程
        ExecutorService service = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            final 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 {
                        latch.countDown();// 每个线程休眠后,会进行一次计数
                    }
                }
            };
            service.submit(runnable);
        }
        System.out.println("等待5个人检查完.....");
        latch.await();// 进行等待
        System.out.println("所有人都完成了工作,进入下一个环节。");
    }
}

等待5个人检查完.....
No.1完成了检查。
No.4完成了检查。
No.3完成了检查。
No.5完成了检查。
No.2完成了检查。
所有人都完成了工作,进入下一个环节。

2. 多等1
/**
 * 描述:     模拟100米跑步,5名选手都准备好了,只等裁判员一声令下,所有人同时开始跑步。
 */
public class CountDownLatchDemo2 {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch begin = new CountDownLatch(1);
        ExecutorService service = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            final 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 + "开始跑步了");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            service.submit(runnable);
        }
        //裁判员检查发令枪...
        Thread.sleep(5000);
        System.out.println("发令枪响,比赛开始!");
        begin.countDown();
    }
}
No.2准备完毕,等待发令枪
No.5准备完毕,等待发令枪
No.4准备完毕,等待发令枪
No.1准备完毕,等待发令枪
No.3准备完毕,等待发令枪
发令枪响,比赛开始!
No.2开始跑步了
No.4开始跑步了
No.5开始跑步了
No.3开始跑步了
No.1开始跑步了
3. 两者结合使用
/**
 * 描述:     模拟100米跑步,5名选手都准备好了,只等裁判员一声令下,所有人同时开始跑步。当所有人都到终点后,比赛结束。
 */
public class CountDownLatchDemo1And2 {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch begin = new CountDownLatch(1);

        CountDownLatch end = new CountDownLatch(5);
        ExecutorService service = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            final 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 + "跑到终点了");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        end.countDown();
                    }
                }
            };
            service.submit(runnable);
        }
        //裁判员检查发令枪...
        Thread.sleep(5000);
        System.out.println("发令枪响,比赛开始!");
        begin.countDown();

        end.await(); // 阻塞,等待所有运动员准备
        System.out.println("所有人到达终点,比赛结束");
    }
}
No.1准备完毕,等待发令枪
No.3准备完毕,等待发令枪
No.2准备完毕,等待发令枪
No.5准备完毕,等待发令枪
No.4准备完毕,等待发令枪
发令枪响,比赛开始!
No.1开始跑步了
No.5开始跑步了
No.2开始跑步了
No.3开始跑步了
No.4开始跑步了
No.5跑到终点了
No.3跑到终点了
No.4跑到终点了
No.2跑到终点了
No.1跑到终点了
所有人到达终点,比赛结束

在这里插入图片描述

3. Semaphore信号量

3.1 作用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.2 应用实例

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.3 使用流程和重要方法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 代码演示
  • 基本用法
package flowcontrol.semaphore;

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

/**
 * 描述:     演示Semaphore用法
 */
public class SemaphoreDemo {
    // 1. 构造方法
    static Semaphore semaphore = new Semaphore(5, true);

    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(50);
        for (int i = 0; i < 100; i++) {
            service.submit(new Task());
        }
        service.shutdown();
    }

    static class Task implements Runnable {

        @Override
        public void run() {
            try {
                // 2. 获取信号量方法
//                semaphore.acquire(3);
                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() + "释放了许可证");
            // 3. 释放信号量方法
            semaphore.release(2);
        }
    }
}

pool-1-thread-2拿到了许可证
pool-1-thread-1拿到了许可证
pool-1-thread-3拿到了许可证
pool-1-thread-5拿到了许可证
pool-1-thread-6拿到了许可证 // 最多并发执行5个线程
pool-1-thread-2释放了许可证
pool-1-thread-3释放了许可证
pool-1-thread-1释放了许可证
pool-1-thread-5释放了许可证
pool-1-thread-4拿到了许可证
pool-1-thread-6释放了许可证
...
  • 特殊用法
    在这里插入图片描述
  • 注意释放数量与获取的数量相等
package flowcontrol.semaphore;

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

/**
 * 描述:     演示Semaphore用法
 */
public class SemaphoreDemo {
    // 1. 构造方法
    static Semaphore semaphore = new Semaphore(3, true);

    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(50);
        for (int i = 0; i < 100; i++) {
            service.submit(new Task());
        }
        service.shutdown();
    }

    static class Task implements Runnable {

        @Override
        public void run() {
            try {
                // 2. 获取信号量方法
                semaphore.acquire(3); // 一次拿到3个许可证
//                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() + "释放了许可证");
            // 3. 释放信号量方法
            semaphore.release(3); // 1次释放3个
        }
    }
}

pool-1-thread-1拿到了许可证
pool-1-thread-1释放了许可证
pool-1-thread-2拿到了许可证
pool-1-thread-2释放了许可证
pool-1-thread-3拿到了许可证
pool-1-thread-3释放了许可证
pool-1-thread-4拿到了许可证
pool-1-thread-4释放了许可证
pool-1-thread-5拿到了许可证
...

3.4 注意点

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4. Candition 接口(条件对象)

4.1 作用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.2 代码演示

演示condition的基本用法
package flowcontrol.condition;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 描述:     演示Condition的基本用法
 */
public class ConditionDemo1 {
    private ReentrantLock lock = new ReentrantLock(); // 绑定在锁上
    private Condition condition = lock.newCondition();

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

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

    public static void main(String[] args) throws InterruptedException {
        ConditionDemo1 conditionDemo1 = new ConditionDemo1();
        new Thread(new Runnable() { // 第二个线程唤醒主线程
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                    conditionDemo1.method2();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        conditionDemo1.method1(); // 要先启动第二个线程,再运行主线程的wait方法
    }
}

条件不满足,开始await
准备工作完成,唤醒其他的线程
条件满足了,开始执行后续的任务

Process finished with exit code 0

实现生产者和消费者模式
package flowcontrol.condition;

import java.util.PriorityQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 描述:     演示用Condition实现生产者消费者模式
 */
public class ConditionDemo2 {

    private int queueSize = 10; // 中介:队列大小
    private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);
    private Lock lock = new ReentrantLock();
    private Condition notFull = lock.newCondition(); // 给生产者用,没有满
    private Condition notEmpty = lock.newCondition();// 给消费者用,不空

    public static void main(String[] args) {
        ConditionDemo2 conditionDemo2 = new ConditionDemo2();
        Producer producer = conditionDemo2.new Producer();
        Consumer consumer = conditionDemo2.new Consumer();
        producer.start();
        consumer.start();
    }

    class Consumer extends Thread {

        @Override
        public void run() {
            consume();
        }

        private void consume() {
            while (true) {
                lock.lock();
                try {
                    while (queue.size() == 0) {
                        System.out.println("队列空,等待数据");
                        try {
                            notEmpty.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    queue.poll();
                    notFull.signalAll(); // 有空闲,唤醒生产者
                    System.out.println("从队列里取走了一个数据,队列共" + queue.size() + "个元素");
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    class Producer extends Thread {

        @Override
        public void run() {
            produce();
        }

        private void produce() {
            while (true) {
                lock.lock();
                try {
                    while (queue.size() == queueSize) {
                        System.out.println("队列满,等待有空余");
                        try {
                            notFull.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    queue.offer(1);
                    notEmpty.signalAll();
                    System.out.println("向队列插入了一个元素,队列剩余空间" + (queueSize - queue.size()));
                } finally {
                    lock.unlock();
                }
            }
        }
    }

}

从队列里取走了一个数据,队列共9个元素
从队列里取走了一个数据,队列共8个元素
向队列插入了一个元素,队列剩余空间1
向队列插入了一个元素,队列剩余空间0
队列满,等待有空余
从队列里取走了一个数据,队列共9个元素
从队列里取走了一个数据,队列共8个元素
从队列里取走了一个数据,队列共7个元素
从队列里取走了一个数据,队列共6个元素
从队列里取走了一个数据,队列共5个元素
从队列里取走了一个数据,队列共4个元素
从队列里取走了一个数据,队列共3个元素
从队列里取走了一个数据,队列共2个元素
从队列里取走了一个数据,队列共1个元素
从队列里取走了一个数据,队列共0个元素
队列空,等待数据
向队列插入了一个元素,队列剩余空间9
向队列插入了一个元素,队列剩余空间8
向队列插入了一个元素,队列剩余空间7
向队列插入了一个元素,队列剩余空间6
向队列插入了一个元素,队列剩余空间5
向队列插入了一个元素,队列剩余空间4
向队列插入了一个元素,队列剩余空间3
向队列插入了一个元素,队列剩余空间2
向队列插入了一个元素,队列剩余空间1
...

4.3 注意点

在这里插入图片描述

  • 并且await更加灵活,可以对一个锁绑定多个condition条件

5. CycliBarrier循环栅栏

5.1 与CountDownLatch的作用比较

在这里插入图片描述
在这里插入图片描述

5.2 代码展示

package flowcontrol.cyclicbarrier;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * 描述:    演示CyclicBarrier
 */
public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() { //5个线程都到了后,触发任务
            @Override
            public void run() {
                System.out.println("这批人都到场了, 大家统一出发!");
            }
        });
        for (int i = 0; i < 10; i++) { // 可重复,循环进行
            new Thread(new Task(i, cyclicBarrier)).start();
        }
    }
    // 执行具体任务
    static class Task implements Runnable{
        private int id;
        private CyclicBarrier cyclicBarrier;

        public Task(int id, CyclicBarrier cyclicBarrier) {
            this.id = id;
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            System.out.println("线程" + id + "现在前往集合地点");
            try {
                Thread.sleep((long) (Math.random()*10000));
                System.out.println("线程"+id+"到了集合地点,开始等待其他人到达");
                cyclicBarrier.await();
                System.out.println("线程"+id+"出发了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }
}

"D:\Program Files\Java\jdk1.8.0_152\bin\java.exe" "-javaagent:D:\program files\JetBrains\IntelliJ IDEA 2020.3.1\lib\idea_rt.jar=53130:D:\program files\JetBrains\IntelliJ IDEA 2020.3.1\bin" -Dfile.encoding=UTF-8 -classpath "D:\program files\Java\jdk1.8.0_152\jre\lib\charsets.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\deploy.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\ext\access-bridge-64.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\ext\cldrdata.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\ext\dnsns.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\ext\jaccess.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\ext\jfxrt.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\ext\localedata.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\ext\nashorn.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\ext\sunec.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\ext\sunjce_provider.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\ext\sunmscapi.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\ext\sunpkcs11.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\ext\zipfs.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\javaws.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\jce.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\jfr.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\jfxswt.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\jsse.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\management-agent.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\plugin.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\resources.jar;D:\program files\Java\jdk1.8.0_152\jre\lib\rt.jar;E:\Java学习笔记\java笔记\2.java基础知识\java并发\concurrency_tools_practice-master\out\production\concurrency_tools_practice-master" flowcontrol.cyclicbarrier.CyclicBarrierDemo
线程1现在前往集合地点
线程2现在前往集合地点
线程0现在前往集合地点
线程3现在前往集合地点
线程4现在前往集合地点
线程6现在前往集合地点
线程7现在前往集合地点
线程5现在前往集合地点
线程8现在前往集合地点
线程9现在前往集合地点
线程6到了集合地点,开始等待其他人到达
线程8到了集合地点,开始等待其他人到达
线程1到了集合地点,开始等待其他人到达
线程4到了集合地点,开始等待其他人到达
线程5到了集合地点,开始等待其他人到达
这批人都到场了, 大家统一出发!
线程5出发了
线程6出发了
线程1出发了
线程8出发了
线程4出发了
线程9到了集合地点,开始等待其他人到达
线程3到了集合地点,开始等待其他人到达
线程7到了集合地点,开始等待其他人到达
线程2到了集合地点,开始等待其他人到达
线程0到了集合地点,开始等待其他人到达
这批人都到场了, 大家统一出发!
线程0出发了
线程9出发了
线程3出发了
线程2出发了
线程7出发了

Process finished with exit code 0

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值