资讯专栏INFORMATION COLUMN

java并发编程学习10--同步器--倒计时门栓

stackfing / 1177人阅读

摘要:每个工作线程在结束前将门栓计数器减一,门栓的计数变为就表明工作完成。常用方法递减锁存器的计数,如果计数到达零,则释放所有等待的线程。使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。

【同步器

java.util.concurrent包包含几个能帮助人们管理相互合作的线程集的类。这些机制具有为线程直间的共用集结点模式提供的‘预制功能’。如果有一个相互合作的线程满足这些行为模式之一,那么应该直接使用提供的类库而不是显示的使用锁与条件的集合。

【倒计时门栓

一个倒计时门栓(CountDownlatch)让一个线程集直到计数变为0.倒计时门栓是一次性的,一旦计数为0就不能再重用了。一个有用的特例是计数值为1的门栓。实现一个只能通过一次的门。线程在门外等待直到另一个线程将计数值变为0。举例来讲,假设一个线程集需要一些初始数据来完成工作。工作线程被启动并在,门外等候,另一个线程准备数据,当数据准备好时,调用countDown(),所有的工作线程就可以继续工作了。然后再使用一个门栓检查什么时候工作线程全部运行完成。每个工作线程在结束前将门栓计数器减一,门栓的计数变为0就表明工作完成。

【常用方法

public void countDown():递减锁存器的计数,如果计数到达零,则释放所有等待的线程。如果当前计数大于零,则将计数减少。如果新的计数为零,出于线程调度目的,将重新启用所有的等待线程。如果当前计数等于零,则不发生任何操作。

public boolean await():使当前线程在锁存器倒计数至零之前一直等待,如果当前计数为零,则此方法立刻返回 true 值。

public boolean await(long timeout,TimeUnit unit) throws InterruptedException:使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。如果当前计数为零,则此方法立刻返回 true 值。如果当前计数大于零,则出于线程调度目的,将禁用当前线程,且在发生以下三种情况之一前,该线程将一直处于休眠状态:

由于调用 countDown() 方法,计数到达零;或者其他某个线程中断当前线程;或者已超出指定的等待时间。如果计数到达零,则该方法返回 true 值。

如果当前线程:在进入此方法时已经设置了该线程的中断状态;或者在等待时被中断,则抛出 InterruptedException,并且清除当前线程的已中断状态。

如果超出了指定的等待时间,则返回值为 false。如果该时间小于等于零,则此方法根本不会等待。

【例子

模拟一个应用程序:在正式开始工作前需要初始化数据,初始化数据使用三个线程,正式执行需要五个线程:

初始化线程

public class InitThread implements Runnable{

    private CountDownLatch downLatch;
    private String name;

    public InitThread(CountDownLatch downLatch, String name){
        this.downLatch = downLatch;
        this.name = name;
    }

    public void run() {
        this.doWork();
        try{
            TimeUnit.SECONDS.sleep(new Random().nextInt(10));
        }catch(InterruptedException ie){
        }
        System.out.println(this.name + "初始化数据完成");
        //计数器减一
        this.downLatch.countDown();
    }
    private void doWork(){
        System.out.println(this.name + "正在初始化数据... ...");
    }

}

初始化线程监视器

/**
 * 检测初始化数据监视器,因为需要判断是否初始化线程全部执行完毕,这里用callable返回结果。runnable不能返回值所以无法判断。
 */
public class InitMonitor implements Callable{

    private ExecutorService executor;
    private CountDownLatch initLatch;
    private List initThreads;

    public InitMonitor(ExecutorService executor){
        this.executor = executor;
        //初始化线程:3个
        initLatch = new CountDownLatch(3);
        initThreads = Arrays.asList(new InitThread(initLatch,"InitOne"),
                                    new InitThread(initLatch,"InitTwo"),
                                    new InitThread(initLatch,"InitThree"));
    }

    public String call() {
        System.out.println("=========初始化START==========");
        initThreads.stream().forEach(initThread -> executor.submit(initThread));
        try {
            initLatch.await();
        } catch (InterruptedException e) {
        }
        System.out.println("***********初始化END*************");
        return "INIT_SUCCESS";
    }
}

工作线程

public class ExecuteThread implements Runnable{

    private CountDownLatch downLatch;
    private String name;

    public ExecuteThread(CountDownLatch downLatch, String name){
        this.downLatch = downLatch;
        this.name = name;
    }

    public void run() {
        this.doWork();
        try{
            TimeUnit.SECONDS.sleep(new Random().nextInt(10));
        }catch(InterruptedException ie){
        }
        System.out.println(this.name + "执行完成");
        //计数器减一
        this.downLatch.countDown();
    }
    private void doWork(){
        System.out.println(this.name + "正在执行... ...");
    }
}

工作线程监视器

public class ExecuteMonitor implements Callable{

    private ExecutorService executor;
    private CountDownLatch executeLatch;
    private List executeThreads;

    public ExecuteMonitor(ExecutorService executor){
        this.executor = executor;
        //执行线程:5个
        executeLatch = new CountDownLatch(5);
        executeThreads = Arrays.asList(new ExecuteThread(executeLatch,"ExecuteOne"),
                                        new ExecuteThread(executeLatch,"ExecuteTwo"),
                                        new ExecuteThread(executeLatch,"ExecuteThree"),
                                        new ExecuteThread(executeLatch,"ExecuteFour"),
                                        new ExecuteThread(executeLatch,"ExecuteFive"));
    }

    public String call() {
        System.out.println("========执行START========");
        executeThreads.stream().forEach(executeThread -> executor.submit(executeThread));
        try {
            executeLatch.await();
        } catch (InterruptedException e) {
        }
        System.out.println("*********执行END*********");
        return "EXECUTE_SUCCESS";
    }
}

应用程序

public class Application implements Runnable{

    private ExecutorService executor;
    private InitMonitor initMonitor;
    private ExecuteMonitor executeMonitor;

    public Application(ExecutorService executor){
        this.executor = executor;
        initMonitor = new InitMonitor(executor);
        executeMonitor = new ExecuteMonitor(executor);
    }
    @Override
    public void run() {
        System.out.println("===============应用程序执行开始====================》》》");
        FutureTask initTask = new FutureTask(initMonitor);
        executor.submit(initTask);
        try {
            //如果初始化成功开始执行工作线程,在调用get()时,如果没有执行完成会自动阻塞,所以这里不需要使用isDone检测。
            if("INIT_SUCCESS".equals(initTask.get())){
                FutureTask executeTask = new FutureTask(executeMonitor);
                executor.submit(executeTask);
                if("EXECUTE_SUCCESS".equals(executeTask.get())){
                    executor.shutdown();
                    System.out.println("===============应用程序执行完毕====================");
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

客户端

客户端一定尽量简介,所有细节全部屏蔽,这里只留下一个可以自定义线程池给用户自行选择

public class Test {

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(10);
        Application application = new Application(executor);
        application.run();
    }
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/68090.html

相关文章

  • java并发编程学习11--同步--栅栏

    摘要:考虑大量线程运行在一次计算的不同部分的情形。一旦所有的线程都到达了这个栅栏,栅栏就撤销,线程可以继续运行。那些已经在等待的线程立即中止的调用。如果在执行屏障操作过程中发生异常,则该异常将传播到当前线程中,并将置于损坏状态。 【同步器 java.util.concurrent包包含几个能帮助人们管理相互合作的线程集的类。这些机制具有为线程直间的共用集结点模式提供的‘预制功能’。如果有一个...

    desdik 评论0 收藏0
  • BATJ都爱问的多线程面试题

    摘要:今天给大家总结一下,面试中出镜率很高的几个多线程面试题,希望对大家学习和面试都能有所帮助。指令重排在单线程环境下不会出先问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。使用可以禁止的指令重排,保证在多线程环境下也能正常运行。 下面最近发的一些并发编程的文章汇总,通过阅读这些文章大家再看大厂面试中的并发编程问题就没有那么头疼了。今天给大家总结一下,面试中出镜率很高的几个多线...

    高胜山 评论0 收藏0
  • java并发编程学习9-同步--信号量

    摘要:为了通过信号量,线程通过调用请求许可。许可的数目是固定的,由此限制了线程通过的数量。当设置为时默认也是,此类不对线程获取许可的顺序做任何保证。 【同步器 java.util.concurrent包包含几个能帮助人们管理相互合作的线程集的类。这些机制具有为线程直间的共用集结点模式提供的‘预制功能’。如果有一个相互合作的线程满足这些行为模式之一,那么应该直接使用提供的类库而不是显示的使用锁...

    Jokcy 评论0 收藏0
  • 并发

    摘要:表示的是两个,当其中任意一个计算完并发编程之是线程安全并且高效的,在并发编程中经常可见它的使用,在开始分析它的高并发实现机制前,先讲讲废话,看看它是如何被引入的。电商秒杀和抢购,是两个比较典型的互联网高并发场景。 干货:深度剖析分布式搜索引擎设计 分布式,高可用,和机器学习一样,最近几年被提及得最多的名词,听名字多牛逼,来,我们一步一步来击破前两个名词,今天我们首先来说说分布式。 探究...

    supernavy 评论0 收藏0
  • 并发

    摘要:表示的是两个,当其中任意一个计算完并发编程之是线程安全并且高效的,在并发编程中经常可见它的使用,在开始分析它的高并发实现机制前,先讲讲废话,看看它是如何被引入的。电商秒杀和抢购,是两个比较典型的互联网高并发场景。 干货:深度剖析分布式搜索引擎设计 分布式,高可用,和机器学习一样,最近几年被提及得最多的名词,听名字多牛逼,来,我们一步一步来击破前两个名词,今天我们首先来说说分布式。 探究...

    ddongjian0000 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<