控制并发流程

控制并发流程

通过控制并发的工具类,帮助程序员更容易让程序之间相互合作,来满足业务逻辑
在这里插入图片描述

CountDownLatch

  • 倒数门闩
  • 倒数结束前,一直处于等待状态,直到倒计时结束了,此线程才继续工作。
  • 主要方法:
    1. CountDownLatch(int count):构造函数,参数count为需要倒数的数值。
    2. await():调用await()方法的线程会被挂起,它会等待count的值为0才会继续执行。
    3. countDown():将count值减1,直到为0时,等待的线程会被唤起。
    4. 扩展用法:多个线程等待多个线程执行完成后,再同时执行。
    5. CountDownLatch是不能够重用的,如果需要重新计数,可以考虑使用CyclicBarrier或者创建新的CountDownLatch。

在这里插入图片描述

/**
 * 质检,5个工人检查,所有都通过,才通过检查
 */
public class CountDOWNlatchDemo1 {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(5);
        ExecutorService service = Executors.newFixedThreadPool(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 {
                        latch.countDown();
                    }
                }
            };
            service.submit(runnable);
        }
        System.out.println("等待5个人检查");
        latch.await();
        System.out.println("所有人都完成了工作,进入下一个环节");
    }
}
/**
 * 模拟100米跑步,5名运动员都准备好,只等裁判一声令下,所有人同时开始跑步
 */
public class CountDOWNlatchDemo2 {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch begin = new CountDownLatch(1);
        ExecutorService executorService = Executors.newFixedThreadPool(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 + "开始跑步了");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            executorService.submit(runnable);
        }
        Thread.sleep(5000);
        System.out.println("发令枪响");
        begin.countDown();
    }
}
/**
 * 模拟100米跑步,5名运动员都准备好,只等裁判一声令下,所有人同时开始跑步
 * 当所有人都到终点后,比赛结束
 */
public class CountDOWNlatchDemo3 {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch begin = new CountDownLatch(1);
        CountDownLatch end = new CountDownLatch(5);
        ExecutorService executorService = Executors.newFixedThreadPool(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 + "跑到终点了");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        end.countDown();
                    }
                }
            };
            executorService.submit(runnable);
        }
        Thread.sleep(5000);
        System.out.println("发令枪响,比赛开始。");
        begin.countDown();

        end.await();
        System.out.println("所有人都到达,比赛结束。");
    }
}

Semaphore信号量

  • Semaphore可以用来管理数量有限的资源使用情况。
  • 信号量的作用是维护一个“许可证”的计数,线程可以“获取”许可证,那信号量剩余的许可证就减1,线程也可以“释放”一个许可证,那信号量剩余的许可证就加1,当信号量所拥有的许可证数量为0,那么下一个还想要获取许可证的线程,就需要等待,直到有另外的线程释放了许可证。
  • 使用:
    1. 初始化Semaphore并指定许可证的数量。
    2. 在需要许可证的代码前加acquire()或者 acquireUnintrruptibly()方法。
    3. 在任务执行结束后,调用release()来释放许可证。
  • 主要方法:
    1. new Semaphore(int permits,boolean fair):设置是否使用公平策略,如果传入true,那么Semaphore会把之前等待的线程放到FIFO的队列里,以便于当有了新的许可证,可以分发给之前等待了最长时间的线程。
    2. tryAcquir():看看现在有没有空闲的许可证,如果有就获取,如果没有的话也没关系,不必陷入阻塞,可以去做别的事,过一会再来查看许可证的空闲情况。
    3. acquire()
    4. acquireUninterruptibly()
    5. release()
    6. 一次性可以获取或释放多个许可证,获取和释放许可证的数量必须一致,否则锁着时间推移,最后许可证数量不足,程序会卡死。
    7. 注意在初始化Semaphore初始化时设置公平性,一般设置true比较合理。
    8. 并不是必须由获取许可证的线程来释放那个许可证,事实上,获取和释放许可证对线程并无要求,也许A获取,由B释放,只要逻辑合理即可。
    9. 信号量除了控制临界区最多同时有N个线程访问外,另一个作用可以实现“条件等待”,例如线程1需要在线程2完成准备工作后才能开始工作,那么就线程1 acquire(),而线程2 完成任务后release(),这样相当于轻量级的CountDownLatch。

在这里插入图片描述

/**
 * 演示Semaphore用法
 */
public class SemaphoreDemo {
    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 {
                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();
        }
    }
}

Condition

  • 当线程1需要等待某个条件的时候,他就去执行condition.await(),一旦执行了await(),线程就会进入阻塞状态。
  • 然后通过另外一个线程,假设是线程2,去执行对用的条件,直到这个条件达成的时候,线程2就会去执行condition.signal()方法,这时JVM就会从被阻塞的线程里找,找到那些等待该condition的线程,当线程1收到可执行信号的时候,它的线程状态就会变成Runnable可执行状态。
  • signalAll()会唤醒所有正在等待的线程。
  • 但是signal()是公平的,只会唤醒那个等待时间最长的线程。
  • 可以说Condition用来代替Object.wait()/notify(),所以在用法和性质上,几乎一致。
  • await方法会自动释放持有的锁,和Object.wait()一样,不需要自己手动先释放锁。
  • 调用await的时候,必须先持有锁,否则会抛出异常,和Object.wait一样。
/**
 * 演示Condition基本用法
 */
public class ConditionDemo1 {
    private ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    void method1() throws InterruptedException {
        lock.lock();
        try {
            System.out.println("条件不满足,开始等待……");
            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();
    }
}
/**
 * 演示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() {
            consumer();
        }

        private void consumer() {
            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() {
            producer();
        }
        private void producer() {
            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();
                }
            }

        }
    }
}

CyclicBarrier循环栅栏

  • CyclicBarrier和CountDownLatch一样,都能阻塞一组线程。
  • 当有大量线程相互配合时,分别计算不同的任务,并且需要最后统一汇总的时候,我们可以使用CyclicBarrier。CyclicBarrier可以构造一个集合点,当某一线程执行完毕,他就会到集合点等待,直到所有线程都到了集合点,那么该栅栏就会被撤销,所有线程再统一出发,执行后面的任务。
  • 例如生活中,三个朋友都到齐了,再统一讨论去哪里吃饭。
  • 和CountDownLatch不同的是,CyclicBarrier可以重复使用。
  • CyclicBarrier等所有线程到达栅栏位置才继续执行,CountDownLatch是等待到计数到0,所以CyclicBarrier是作用于线程,CountDownLatch作用于事件。
/**
 * 演示CyclicBarrier
 */
public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() {
            @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 (BrokenBarrierException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值