Java多线程之CountDownLatch、CyclicBarrier和Semaphore

简介:

转自:http://www.liubey.org/countdownlatch_vs_cyclicbarrier/


概述

CountDownLatch : 一个线程(或者多个), 等待另外N个线程完成某个事情之后才能执行。

CyclicBarrier : N个线程相互等待,任何一个线程完成之前,所有的线程都必须等待。

Semaphore:可以控制某个资源可被同时访问的个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。



举例

1:CountDownLatch

使用场景举例:

体育课时老师拿着秒表测试同学的800米成绩,那需求就是很简单了,老师在起跑处组织大家一起跑的瞬间按下秒表计时开始,然后再终点处等待最后一个学生通过终点后开始汇集学生成绩。

API相关:

1)await(),阻塞等待,直到计数器清零
2)await(int timeout, TimeUnit unit),使线程阻塞,除非被中断或者超过等待的最大时间
如果达到计数器清零,则await返回true,如果等待超过了最大的等待时间,则返回false
3)countDown(),计数器减一,当计数器清零时,await的线程被唤醒,线程继续执行
4)getCount (),获取当前计数器的大小


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

public class TeacherWithStopwatch {
    public static final int NUMBER_OF_STUDENT = 10;
    public static final int NUMBER_OF_TEACHER = 1;
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        CountDownLatch studentSignal = new CountDownLatch(NUMBER_OF_STUDENT);
        CountDownLatch teacherSignal = new CountDownLatch(NUMBER_OF_TEACHER);
        for (int i = 0; i < NUMBER_OF_STUDENT; i++) {
            executor.execute(new Student(i, studentSignal, teacherSignal));
        }
        try {
            System.out.println("各就各位!开跑!");
            teacherSignal.countDown();
            studentSignal.await();
            System.out.println("结果发送到汇报成绩的系统");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            executor.shutdown();
        }
    }
}


    import java.util.concurrent.CountDownLatch;
public class Student implements Runnable {
    private int id;
    private CountDownLatch studentSignal;
    private CountDownLatch teacherSignal;
    public Student(int id, CountDownLatch studentSignal,
        CountDownLatch teacherSignal) {
        this.id = id;
        this.studentSignal = studentSignal;
        this.teacherSignal = teacherSignal;
    }
    @Override
    public void run() {
        try {
            teacherSignal.await();
            System.out.println("学生" + id + "起跑...");
            System.out.println("学生" + id + "到达终点。");
            studentSignal.countDown();
            System.out.println("学生" + id + "继续干其他事情");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


2:CyclicBarrier

使用场景举例:

有四个游戏玩家玩游戏,游戏有三个关卡,每个关卡必须要所有玩家都到达后才能允许通关。
其实这个场景里的玩家中如果有玩家A先到了关卡1,他必须等待其他所有玩家都到达关卡1时才能通过。
也就是说线程之间需要互相等待,这和CountDownLatch的应用场景有区别,
CountDownLatch里的线程是到了运行的目标后继续干自己的其他事情,而这里的线程需要等待其他线程后才能继续完成下面的工作。

API相关:

public CyclicBarrier(int parties) 创建一个新的 CyclicBarrier,
它将在给定数量的参与者(线程)处于等待状态时启动,但它不会在启动 barrier 时执行预定义的操作。
public CyclicBarrier(int parties, Runnable barrierAction) 创建一个新的 CyclicBarrier,
它将在给定数量的参与者(线程)处于等待状态时启动,并在启动 barrier 时执行给定的屏障操作,
该操作由最后一个进入 barrier 的线程执行。
public int await() throws InterruptedException, BrokenBarrierException 在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。
public int await(long timeout,TimeUnit unit) throws InterruptedException, BrokenBarrierException,TimeoutException
在所有参与者都已经在此屏障上调用 await 方法之前将一直等待,或者超出了指定的等待时间。
public int getNumberWaiting() 返回当前在屏障处等待的参与者数目。此方法主要用于调试和断言。
public int getParties() 返回要求启动此 barrier 的参与者数目。
public boolean isBroken() 查询此屏障是否处于损坏状态。
public void reset() 将屏障重置为其初始状态。


public class GameBarrier {
    public static final int NUMBER_OF_PLAYERS = 4;
    public static void main(String[] args) {
    ExecutorService executor = Executors.newFixedThreadPool(NUMBER_OF_PLAYERS);
    CyclicBarrier barrier = new CyclicBarrier(NUMBER_OF_PLAYERS, new Runnable() {
        @Override
        public void run() {
            System.out.println("所有玩家通过第一关!");
        }
    });
    for (int i = 0; i < NUMBER_OF_PLAYERS; i++) {
        executor.execute(new Player(i, barrier));
    }
    executor.shutdown();
    }
}

public class Player implements Runnable {
    private CyclicBarrier cyclicBarrier;
    private int id;
    public Player(int id, CyclicBarrier cyclicBarrier) {
        this.cyclicBarrier = cyclicBarrier;
        this.id = id;
    }
    @Override
    public void run() {
        try {
            System.out.println("玩家" + id + "通过第一关...");
            cyclicBarrier.await();
            System.out.println("玩家" + id + "进入第二关...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
        e.printStackTrace();
        }
    }
}

3:Semaphore

使用场景举例:

一个资源池只能同时五个人使用,那么十个人来轮询使用的情况就是每个人都先申请资源,使用完归还于下一个人使用。

API相关:

构造器中的fairness为true时,Semaphore保证各线程以后进先出(FIFO)的方式获得信号量。如果fairness为false,则不保证这种顺序,允许各线程之间的“讨价还价”。

tryAcquire与release为主要方法


public class Person implements Runnable {
    private SemaphorePool pool;
    private int id;
    public Person(int id, SemaphorePool pool) {
        this.pool = pool;
        this.id = id;
    }
    @Override
    public void run() {
        try {
            pool.applyResource();
            System.out.println("人物" + id + "进入");
            Thread.sleep(1000);
            System.out.println("人物" + id + "离开");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            pool.releaseResource();
        }
    }
}

public class SemaphorePool {
    private Semaphore canExecuteCount = new Semaphore(5, true);
    private static final int TRY_EXECUTE_TIMEOUT = 20;
    public boolean applyResource() {
        boolean canExecute = false;
        try {
            canExecute = canExecuteCount.tryAcquire(1, TRY_EXECUTE_TIMEOUT,
                TimeUnit.SECONDS);
        } catch (InterruptedException e) {}
        return canExecute;
    }
    public void releaseResource() {
        canExecuteCount.release(1);
    }
}


public class TestSemaphore {
    public static final int NUMBER_OF_PERSONS = 10;
    public static final SemaphorePool pool = new SemaphorePool();
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(NUMBER_OF_PERSONS);
        for(int i=0;i<NUMBER_OF_PERSONS;i++) {
            executor.execute(new Person(i, pool));
        }
        executor.shutdown();
    }
}
本文转自 古道卿 51CTO博客,原文链接:http://blog.51cto.com/gudaoqing/1550156


相关文章
|
5月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
269 1
|
5月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
288 1
|
6月前
|
Java 开发者
Java并发编程:CountDownLatch实战解析
Java并发编程:CountDownLatch实战解析
530 100
|
6月前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
Java 数据库 Spring
249 0
|
6月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
439 16
|
7月前
|
缓存 并行计算 安全
关于Java多线程详解
本文深入讲解Java多线程编程,涵盖基础概念、线程创建与管理、同步机制、并发工具类、线程池、线程安全集合、实战案例及常见问题解决方案,助你掌握高性能并发编程技巧,应对多线程开发中的挑战。
|
Java 数据库 容器
java中使用Semaphore构建阻塞对象池
java中使用Semaphore构建阻塞对象池
|
7月前
|
数据采集 存储 前端开发
Java爬虫性能优化:多线程抓取JSP动态数据实践
Java爬虫性能优化:多线程抓取JSP动态数据实践
|
8月前
|
Java API 调度
从阻塞到畅通:Java虚拟线程开启并发新纪元
从阻塞到畅通:Java虚拟线程开启并发新纪元
421 83