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


目录
打赏
0
0
0
0
344
分享
相关文章
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
55 23
【JAVA】封装多线程原理
Java 中的多线程封装旨在简化使用、提高安全性和增强可维护性。通过抽象和隐藏底层细节,提供简洁接口。常见封装方式包括基于 Runnable 和 Callable 接口的任务封装,以及线程池的封装。Runnable 适用于无返回值任务,Callable 支持有返回值任务。线程池(如 ExecutorService)则用于管理和复用线程,减少性能开销。示例代码展示了如何实现这些封装,使多线程编程更加高效和安全。
|
3月前
|
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
Java 多线程 面试题
Java 多线程 相关基础面试题
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
3月前
|
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
287 2
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
3月前
|
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
80 3
Java中的多线程编程:从基础到实践
本文深入探讨了Java多线程编程的核心概念和实践技巧,旨在帮助读者理解多线程的工作原理,掌握线程的创建、管理和同步机制。通过具体示例和最佳实践,本文展示了如何在Java应用中有效地利用多线程技术,提高程序性能和响应速度。
107 1

热门文章

最新文章