Java多线程-控制并发流程

1、什么是控制并发流程

控制并发流程的工具类,作用是帮助我们程序员更容易让线程之间合作

让线程之间相互配合,来满足业务逻辑

比如让线程A等待线程B执行完毕后在执行等策略

2、CountDownLatch倒计时门闩

例子:购物拼团,人满发车

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

2.1、常用方法介绍

  • new CountDownLatch(5);设置有几个数
  • latch.countDown();谁减一谁调用countDown
  • latch.await();谁等待谁调用await

2.2、一等多

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

/**
 * @Classname CountLatchDemo1
 * @Description 工厂中,质检,5个人检查,所有人都认为通过,才通过
 * @Date 2021/5/18 22:04
 * @Created by WangXiong
 */
public class CountDownLatchDemo1 {

    public static void main(String[] args) {
        final CountDownLatch latch = new CountDownLatch(5);
        ExecutorService service = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            final int no = i + 1;
            Runnable runnable = new Runnable() {

                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个工人检查完毕....");
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("所有人都完成了工作,进入下一个环节");
        service.shutdown();
    }
}

测试

2.3、多等一

多个线程等待一个线程,在同一出发

/**
 * @Classname CountDownLatchDemo2
 * @Description 模拟100米跑步,5名选手都准备好了,只等裁判一声令下,所有人同时跑步
 * @Date 2021/5/18 22:27
 * @Created by WangXiong
 */
public class CountDownLatchDemo2 {

    public static void main(String[] args) {
        final 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() {

                public void run() {
                    System.out.println("No." + no + "准备完毕,等待发令枪");
                    try {
                        begin.await();
                        System.out.println("No." + no + "开始跑步了");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            service.submit(runnable);
        }

        try {
            //模拟裁判员检查发令枪
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发令枪响,比赛开始!");
        begin.countDown();
        service.shutdown();

    }
}

测试

2.4、升级用法

五个远动员等待发令枪响,裁判等待五个运动员跑步结束

/**
 * @Classname CountDownLatchDemo2
 * @Description 模拟长跑,我们需要模拟发令枪,以及到达终点的结束比赛
 * @Date 2021/5/18 22:27
 * @Created by WangXiong
 */
public class CountDownLatchDemo1AndDemo2 {

    public static void main(String[] args) {
        final CountDownLatch begin = new CountDownLatch(1);
        final 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() {

                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();
                    }
                }
            };
            service.submit(runnable);
        }

        try {
            //模拟裁判员检查发令枪
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发令枪响,比赛开始!");
        begin.countDown();

        try {
            end.await();
            System.out.println("所有人到达终点,比赛结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        service.shutdown();

    }
}

测试

2.5、注意点

CountDownLatch是不能够重用的

3、Semaphore信号量

用来限制有限资源的使用

信号量,可以类比为生活中的许可证

使用流程:

  • 初始化Semaphore并指定许可证的数量
  • 在执行方法前需要选获取许可证
  • 执行完方法后需要释放许可证

常用方法

  • new Semaphore(int permits, boolean fair):这里可以设置是否需要公平策略
  • tryAcquire()判断是否可以拿到许可证
  • acquire()获取许可证,可以响应中断
  • acquireUninterruptibly()获取许可证,不响应中断
  • releas()释放许可证

3.1、代码演示

/**
 * @Classname SemaphoreDemo1
 * @Description 多个线程去等待许可证,没有获取到许可证的线程进行排查
 * @Date 2021/5/18 22:56
 * @Created by WangXiong
 */
public class SemaphoreDemo1 {
    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());
        }
    }

    static class Task implements Runnable{

        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();
        }
    }
}

测试

3.2、信号量特殊用法

信号量一次性可以获取一个或多个信号量,返回也可以返回一个或多个。获取和返回的许可证个数必须一致

public class SemaphoreDemo1 {
    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());
        }
    }

    static class Task implements Runnable{

        public void run() {
            try {
                semaphore.acquire(3);
            } 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(3);
        }
    }
}

测试

4、 Condition接口(条件对象)

4.1、Condition作用

线程1需要等待某个条件的时候,他就会执行Condition.await()方法,进入阻塞,线程2执行完后,会调用signal()方法, 唤醒线程1,继续执行

4.2、代码演示

/**
 * @Classname ConditionDemo1
 * @Description 演示condition基本用法
 * @Date 2021/5/18 23:11
 * @Created by WangXiong
 */
public class ConditionDemo1 {
    private ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    void method1(){
        lock.lock();
        try{
            System.out.println("条件不满足,开始await");
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

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

    public static void main(String[] args) {
        final ConditionDemo1 demo1 = new ConditionDemo1();
        //自己沉睡不能自己唤醒自己

        // demo1.method2();
        new Thread(new Runnable() {
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                demo1.method2();
            }
        }).start();

        demo1.method1();
    }
}

测试

4.3、condition实现生产者消费者模式

/**
 * @Classname ConditionDemo2
 * @Description 演示用Condition实现生产者和消费者模式
 * @Date 2021/5/19 21:53
 * @Created by WangXiong
 */
public class ConditionDemo2 {

    private int queueSize = 10;
    private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);
    private Lock lock = new ReentrantLock();
    private Condition noFull = lock.newCondition();
    private Condition noEmpty = lock.newCondition();

    class Consumer extends Thread{
        @Override
        public void run() {
            while (true){
                lock.lock();
                try{
                    while (queue.size() == 0){
                        System.out.println("队列空,等待传递数据");
                        try {
                            noEmpty.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    queue.poll();
                    noFull.signalAll();
                    System.out.println("从队列中取走了一个数据,队列剩余"+queue.size() + "元素");
                }finally {
                    lock.unlock();
                }
            }
        }
    }

    class Produce extends Thread{
        @Override
        public void run() {
            while (true){
                lock.lock();
                try{
                    while (queue.size() == queueSize){
                        System.out.println("队列满,等待有空余");
                        try {
                            noFull.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    queue.offer(1);
                    noEmpty.signalAll();
                    System.out.println("向队列插入了一个数据,队列剩余" + (queueSize - queue.size()) + "元素");
                }finally {
                    lock.unlock();
                }
            }
        }
    }

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

测试

5、CyckuBarrier循环栅栏

和CountDownLatch很类似,都能阻塞一组线程

/**
 * @Classname CycliBarrierDemo
 * @Description 演示CyclicBarrier
 * @Date 2021/5/19 22:07
 * @Created by WangXiong
 */
public class CycliBarrierDemo {

    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() {
            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;
        }

        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();
            }

        }
    }
}

测试

CyclicBarrier和CountDownLatch的区别

CyclicBarrier针对的是线程,CountDownLatch针对的是事件

CyclicBarrier可以重复使用,CountDownLatch不能重复使用

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值