菜单

JUC——线程同步帮衬工具类(Semaphore,CountDownLatch,CyclicBarrier)

2019年5月23日 - Java

Exchanger沟通空间

设若明天有多少个线程,3个线程肩负生产数量,其余八个线程担任消费数据,那么这么些八个线程之间必然会存在1个集体的区域,那么那些区域的落到实处在JUC包里面称为Exchanger

java.util.concurrent.Exchanger类表示一种八个线程可以拓展相互交换对象的相会点。

图片 1

Exchanger类中定义的方法如下:

轨范:使用Exchanger达成交流管理

package so.strong.mall.concurrent;
import java.util.concurrent.Exchanger;
import java.util.concurrent.TimeUnit;

public class ExchanerDemo {
    public static void main(String[] args) {
        final Exchanger<String> exchanger = new Exchanger<>(); //准备一个交换空间
        for (int i = 0; i < 3; i++) { //3个消费者
            new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            String data = exchanger.exchange(null);
                            TimeUnit.SECONDS.sleep(2);
                            if (data != null) {
                                System.out.println("[" + Thread.currentThread().getName() + "]取得数据:" + data);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }, "消费者-" + i).start();
        }

        for (int i = 0; i < 2; i++) { //2个生产者
            final int temp = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 2; j++) {
                        String data = "iTermis-" + temp + "-" + j;
                        try {
                            TimeUnit.SECONDS.sleep(2); //让生产者节奏放慢
                            exchanger.exchange(data);
                            System.out.println("[" + Thread.currentThread().getName() + "]生产了数据:" + data);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }, "生产者-" + i).start();
        }
    }
}

[生产者-1]生产了数据:iTermis-1-0
[生产者-1]生产了数据:iTermis-1-1
[消费者-1]取得数据:iTermis-1-0
[生产者-0]生产了数据:iTermis-0-0
[生产者-0]生产了数据:iTermis-0-1
[消费者-2]取得数据:iTermis-0-1

  

锁的体制从全部的运转转态来说主旨正是:阻塞,解除阻塞,但是借使壹味是这一点成效,那么JUC并无法称为一个不错的线程开垦框架,不过是因为在juc里面提供了汪洋有益于的同台工具帮忙类。

CompletableFuture线程回调

现行反革命思索叁个场景,举例:使用炮兵轰炸某一指标

图片 2

有着的施行线程在收到到命令从前都要跻身到阻塞状态之中,从来到接受到现实的下令之后才会实行下一步操作管理。

java.util.concurrent.CompletableFutureJava8中增多的3个类,该类的根本功效是提供了新的不②法门来成功异步管理,包涵合成和组成事件的非阻塞情势。

图片 3

CompletableFuture类中有如下的办法:

模范:使用CompletableFuture达成炮兵轰炸操作

package com.itermis.concurrent;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class CompletableFutureDemo {
    public static void main(String[] args) throws Exception {
        CompletableFuture<String> future = new CompletableFuture<>();
        for (int i = 0; i < 4; i++) {
            new Thread(() -> {
                System.out.println("BEFORE[" + Thread.currentThread().getName() + "]进入炮兵阵地,等待命令,准备开火。");
                try {
                    String cmd = future.get(); //接收命令
                    if ("fire".equals(cmd)) {
                        System.out.println("AFTER[" + Thread.currentThread().getName() + "]接收到命令,立刻开火,干死那个死胖子。。");
                    }
                    if ("cancel".equals(cmd)) {
                        System.out.println("AFTER[" + Thread.currentThread().getName() + "]收到撤退命令,回家睡觉。。");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, "炮兵-" + i).start();
        }
        TimeUnit.SECONDS.sleep(3); //等待3秒钟
        future.complete("cancel"); //给出了执行命令
    }
}

BEFORE[炮兵-1]进入炮兵阵地,等待命令,准备开火。
BEFORE[炮兵-0]进入炮兵阵地,等待命令,准备开火。
BEFORE[炮兵-2]进入炮兵阵地,等待命令,准备开火。
BEFORE[炮兵-3]进入炮兵阵地,等待命令,准备开火。
//sleep 3 秒
AFTER[炮兵-1]收到撤退命令,回家睡觉。。
AFTER[炮兵-0]收到撤退命令,回家睡觉。。
AFTER[炮兵-2]收到撤退命令,回家睡觉。。
AFTER[炮兵-3]收到撤退命令,回家睡觉。。

 该类的处理紧若是成立在Future线程模型的底子之上的贯彻操作。

对此本类来说,除了上述的行使方法之外还是能够动用异步的线程执行措施管理。在创立CompletableFuture类对象的时候还是能利用这几个类之中提供的1种静态方法:

public static CompletableFuture<Void> runAsync(Runnable runnable)

典范:更动完结格局达成上述轰炸操作:

package com.itermis.concurrent;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class CompletableFutureDemoII {
    public static void main(String[] args) {
        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            System.out.println("[FUTURE]将军正在温柔乡里美梦了,等着将军睡醒开炮..");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("[FUTURE]将军醒了,开始干活了..");
        });
        for (int i = 0; i < 4; i++) {
            new Thread(() -> {
                System.out.println("BEFORE[" + Thread.currentThread().getName() + "]进入炮兵阵地,等待命令,准备开火。");
                try {
                    System.out.println("AFTER[" + Thread.currentThread().getName() + "]接收到命令,立刻开火,干死那个死胖子。。" + future.get());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, "炮兵-" + i).start();
        }
    }
}

[FUTURE]将军正在温柔乡里美梦了,等着将军睡醒开炮..
BEFORE[炮兵-1]进入炮兵阵地,等待命令,准备开火。
BEFORE[炮兵-0]进入炮兵阵地,等待命令,准备开火。
BEFORE[炮兵-2]进入炮兵阵地,等待命令,准备开火。
BEFORE[炮兵-3]进入炮兵阵地,等待命令,准备开火。
// sleep 3秒
[FUTURE]将军醒了,开始干活了..
AFTER[炮兵-2]接收到命令,立刻开火,干死那个死胖子。。null
AFTER[炮兵-0]接收到命令,立刻开火,干死那个死胖子。。null
AFTER[炮兵-3]接收到命令,立刻开火,干死那个死胖子。。null
AFTER[炮兵-1]接收到命令,立刻开火,干死那个死胖子。。null

CompletableFuture这几个类最大的便宜是提供有全数等待线程的推行触发点。

 

Semaphore信号量

Semaphore平日用于限制能够访问一些财富(物理or逻辑)的线程数目。

比方,大家排队去银行办理专门的学业,可是唯有多个银行窗口提供劳动,来了十一位索要办理专业,所以那拾2个排队的人口须要各种使用那多少个事情窗口来办理职业。

 

观测塞马phore类的宗旨概念:

public class Semaphore extends Object implements Serializable

Semaphore类中定义的方法有如下多少个:

 模范:完毕银行排队事务办理

package so.strong.mall.concurrent;
import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreDemo {
    public static void main(String[] args) {
        final Semaphore semaphore = new Semaphore(2); //现在允许操作的资源一共有2个
        final Random random = new Random(); //模拟每一个用户办理业务的时间
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() { //每一个线程就是一个要办理业务的人员
                    if (semaphore.availablePermits() > 0) { //现有空余窗口
                        System.out.println("[" + Thread.currentThread().getName() + "]进入银行,没有人办理业务");
                    } else { //没有空余位置
                        System.out.println("[" + Thread.currentThread().getName() + "]排队等候办理业务");
                    }
                    try {
                        semaphore.acquire(); //从信号量中获得操作许可
                        System.out.println("[" + Thread.currentThread().getName() + "]开始办理业务");
                        TimeUnit.SECONDS.sleep(random.nextInt(10)); //模拟办公延迟
                        System.out.println("[" + Thread.currentThread().getName() + "]结束业务办理");
                        semaphore.release(); //当前线程离开办公窗口
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, "顾客-" + i).start();
        }
    }
}

[顾客-0]进入银行,没有人办理业务
[顾客-0]开始办理业务
[顾客-1]进入银行,没有人办理业务
[顾客-1]开始办理业务
[顾客-2]排队等候办理业务
[顾客-3]排队等候办理业务
[顾客-4]排队等候办理业务
[顾客-5]排队等候办理业务
[顾客-6]排队等候办理业务
[顾客-7]排队等候办理业务
[顾客-8]排队等候办理业务
[顾客-9]排队等候办理业务
[顾客-0]结束业务办理
[顾客-2]开始办理业务
[顾客-1]结束业务办理
[顾客-3]开始办理业务
[顾客-2]结束业务办理
[顾客-4]开始办理业务
[顾客-3]结束业务办理
[顾客-5]开始办理业务
[顾客-4]结束业务办理
[顾客-6]开始办理业务
[顾客-5]结束业务办理
[顾客-7]开始办理业务
[顾客-7]结束业务办理
[顾客-8]开始办理业务
[顾客-6]结束业务办理
[顾客-9]开始办理业务
[顾客-8]结束业务办理

这种功率信号量的管理在实际支出中有怎么着用啊?举例,今后对于数据库的一连一共有一个一连,那么也可能有10个用户等待进行数据库操作,能够运用的连日个数为一个,那样13个用户就必要排队依次使用那五个三番五次来进展数据库操作。

 

CountDownLatch闭锁

CoundDownLatch叙述的是三个计数的滑坡,上面首先观看二个顺序的简约难点:

范例:编写一个简便的10贰线程开垦

package so.strong.mall.concurrent;
public class CountDownDemo {
    public static void main(String[] args) {
        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("["+Thread.currentThread().getName()+"]线程应用执行完毕");
                }
            },"线程对象-"+i).start();
        }
        System.out.println("[***主线程***]所有的程序执行完毕");
    }
}

[***主线程***]所有的程序执行完毕
[线程对象-1]线程应用执行完毕
[线程对象-0]线程应用执行完毕  

现今能够开掘,对于此时应当保障全体的线程推行完结后在施行顺序的输出计算,就好比:旅游团集结职员乘车离开。应该保险具有的线程都实践完结了(钦点个数的线程),那样的话就无法不做3个计数管理。

CoundDownLatch那一个类能够使1个线程等待其余线程达成各自的干活后再进行。例如,应用程序的主线程希望在负担运维框架服务的线程已经起步全数的框架服务之后再施行。

CoundDownLatch是经过一个计数器来达成的,计数器的初阶值为线程的多寡。每当贰个线程达成了协和的任务后,计数器的值就能够减一。当计数器值达到0时,它象征全体的线程已经完毕了职分,然后在关掉上伺机的线程就能够过来实施职分。

图片 4

 

CoundDownLatch类之中的常用方法有如下多少个:

范例:利用CountDownLatch解决从前的宏图难点

package so.strong.mall.concurrent;
import java.util.concurrent.CountDownLatch;

public class CountDownDemo {
    public static void main(String[] args) throws Exception {
        final CountDownLatch countDownLatch = new CountDownLatch(2); //2个线程全部执行完毕后可继续执行
        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("[" + Thread.currentThread().getName() + "]线程应用执行完毕");
                    countDownLatch.countDown(); //减少等待的线程个数
                }
            }, "线程对象-" + i).start();
        }
        countDownLatch.await(); //等待计数的结束(个数为0)
        System.out.println("[***主线程***]所有的程序执行完毕");
    }
}

[线程对象-0]线程应用执行完毕
[线程对象-1]线程应用执行完毕
[***主线程***]所有的程序执行完毕

  

 CyclicBarrier栅栏

CyclicBarrierCountDownLatch是老大周围的,CyclicBarrier主旨的概念是介于设置1个等候线程的多少边界,达到了此边界之后进展实施。

CyclicBarrier类是一个1块协理类,它同意1组线程相互等待,直到抵达有个别公共屏障点(Common
Barrier Point)。

CyclicBarrier类是1种共同机制,它亦可对管理局地算法的线程完毕同。换句话讲,它便是贰个具有线程必须等待的多少个栅栏,直到全体线程都达到此处,然后所无线程技艺够持续做别的作业。

由此调用CyclicBarrier对象的await()办法,多个线程能够落成互动等待。一旦N个线程在守候CyclicBarrier高达,全部线程将被放飞掉去继续试行。

 

CyclicBarrier类的重大措施如下:

表率:观看CyclicBarrier举行等待管理

package so.strong.mall.concurrent;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;

public class CyclicBarrierDemo {
    public static void main(String[] args) throws Exception{
        final CyclicBarrier cyclicBarrier = new CyclicBarrier(2); //当凑够2个线程金进行触发
        for (int i = 0; i < 3 ; i++) {
            final int second = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("["+Thread.currentThread().getName()+"]-等待开始");
                    try {
                        TimeUnit.SECONDS.sleep(second);
                        cyclicBarrier.await(); //等待处理
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    System.out.println("["+Thread.currentThread().getName()+"]-等待结束");
                }
            },"娱乐者-"+i).start();
        }
    }
}

 

[娱乐者-0]-等待开始
[娱乐者-1]-等待开始
[娱乐者-2]-等待开始
[娱乐者-1]-等待结束
[娱乐者-0]-等待结束

  若是不想一向等候则能够安装超时时间,则超越了守候时间将来将会现出”TimeoutException”。

package so.strong.mall.concurrent;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;

public class CyclicBarrierDemo {
    public static void main(String[] args) throws Exception{
        final CyclicBarrier cyclicBarrier = new CyclicBarrier(2); //当凑够2个线程金进行触发
        for (int i = 0; i < 3 ; i++) {
            final int second = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("["+Thread.currentThread().getName()+"]-等待开始");
                    try {
                        TimeUnit.SECONDS.sleep(second);
                        cyclicBarrier.await(6,TimeUnit.SECONDS); //等待处理
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    System.out.println("["+Thread.currentThread().getName()+"]-等待结束");
                }
            },"娱乐者-"+i).start();
        }
    }
}

[娱乐者-0]-等待开始
[娱乐者-1]-等待开始
[娱乐者-2]-等待开始
[娱乐者-1]-等待结束
[娱乐者-0]-等待结束
Disconnected from the target VM, address: '127.0.0.1:63717', transport: 'socket'
[娱乐者-2]-等待结束
java.util.concurrent.TimeoutException
    at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
    at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:427)
    at so.strong.mall.concurrent.CyclicBarrierDemo$1.run(CyclicBarrierDemo.java:21)
    at java.lang.Thread.run(Thread.java:745)

 

CyclicBarrier还或然有四个特征是能够开始展览重新恢复设置管理

轨范:重新设置管理

package so.strong.mall.concurrent;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;


public class CyclicBarrierResetDemo {
    public static void main(String[] args) throws Exception {
        final CyclicBarrier cb = new CyclicBarrier(2); //当凑够2个线程就进行触发
        for (int i = 0; i < 3; i++) {
            final int second = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("[" + Thread.currentThread().getName() + "]-等待开始");
                    try {
                        if (second == 2) {
                            cb.reset(); //重置
                            System.out.println("[重置处理****]" + Thread.currentThread().getName());
                        } else {
                            TimeUnit.SECONDS.sleep(second);
                            cb.await(6,TimeUnit.SECONDS);//等待处理
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    System.out.println("[" + Thread.currentThread().getName() + "]-等待结束");
                }
            }, "娱乐者-" + i).start();
        }
    }
}

[娱乐者-0]-等待开始
[娱乐者-1]-等待开始
[娱乐者-2]-等待开始
[重置处理****]娱乐者-2
[娱乐者-2]-等待结束
[娱乐者-1]-等待结束
[娱乐者-0]-等待结束

 

CountDownLatch与CyclicBarrier的区别

 

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图