博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java多线程打辅助的三个小伙子
阅读量:5130 次
发布时间:2019-06-13

本文共 7855 字,大约阅读时间需要 26 分钟。

前言

之前学多线程的时候没有学习线程的同步工具类(辅助类)。ps:当时觉得暂时用不上,认为是挺高深的知识点就没去管了..

在前几天,朋友发了一篇比较好的Semaphore文章过来,然后在浏览博客的时候又发现面试还会考,那还是挺重要的知识点。于是花了点时间去了解一下。

Java为我们提供了三个同步工具类

  • CountDownLatch(闭锁)
  • CyclicBarrier(栅栏)
  • Semaphore(信号量)

这几个工具类其实说白了就是为了能够更好控制线程之间的通讯问题~

一、CountDownLatch

1.1CountDownLatch简介

  • A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

简单来说:CountDownLatch是一个同步的辅助类,允许一个或多个线程一直等待直到其它线程完成它们的操作。

它常用的API其实就两个:await()countDown()

164d942fd946ec7d?w=468&h=298&f=png&s=14667

使用说明:

  • count初始化CountDownLatch,然后需要等待的线程调用await方法。await方法会一直受阻塞直到count=0。而其它线程完成自己的操作后,调用countDown()使计数器count减1。当count减到0时,所有在等待的线程均会被释放
  • 说白了就是通过count变量来控制等待,如果count值为0了(其他线程的任务都完成了),那就可以继续执行。

1.2CountDownLatch例子

例子:3y现在去做实习生了,其他的员工还没下班,3y不好意思先走,等其他的员工都走光了,3y再走。

import java.util.concurrent.CountDownLatch;public class Test {    public static void main(String[] args) {        final CountDownLatch countDownLatch = new CountDownLatch(5);        System.out.println("现在6点下班了.....");        // 3y线程启动        new Thread(new Runnable() {            @Override            public void run() {                           try {                    // 这里调用的是await()不是wait()                    countDownLatch.await();                } catch (InterruptedException e) {                    e.printStackTrace();                }                System.out.println("...其他的5个员工走光了,3y终于可以走了");            }        }).start();        // 其他员工线程启动        for (int i = 0; i < 5; i++) {            new Thread(new Runnable() {                @Override                public void run() {                    System.out.println("员工xxxx下班了");                    countDownLatch.countDown();                }            }).start();        }    }}

输出结果:

164d942f79acba92?w=966&h=309&f=png&s=14967

再写个例子:3y现在负责仓库模块功能,但是能力太差了,写得很慢,别的员工都需要等3y写好了才能继续往下写。

import java.util.concurrent.CountDownLatch;public class Test {    public static void main(String[] args) {        final CountDownLatch countDownLatch = new CountDownLatch(1);        // 3y线程启动        new Thread(new Runnable() {            @Override            public void run() {                try {                    Thread.sleep(5);                } catch (InterruptedException e) {                    e.printStackTrace();                }                System.out.println("3y终于写完了");                countDownLatch.countDown();            }        }).start();        // 其他员工线程启动        for (int i = 0; i < 5; i++) {            new Thread(new Runnable() {                @Override                public void run() {                    System.out.println("其他员工需要等待3y");                    try {                        countDownLatch.await();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    System.out.println("3y终于写完了,其他员工可以开始了!");                }            }).start();        }    }}

输出结果:

164d942fde3ef945?w=1054&h=502&f=png&s=37396

参考资料:

二、CyclicBarrier

2.1CyclicBarrier简介

  • A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point. CyclicBarriers are useful in programs involving a fixed sized party of threads that must occasionally wait for each other. The barrier is called cyclic because it can be re-used after the waiting threads are released.

简单来说:CyclicBarrier允许一组线程互相等待,直到到达某个公共屏障点。叫做cyclic是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用(对比于CountDownLatch是不能重用的)

使用说明:

  • CountDownLatch注重的是等待其他线程完成,CyclicBarrier注重的是:当线程到达某个状态后,暂停下来等待其他线程,所有线程均到达以后,继续执行。

2.2CyclicBarrier例子

例子:3y和女朋友约了去广州夜上海吃东西,由于3y和3y女朋友住的地方不同,自然去的路径也就不一样了。于是他俩约定在体育西路地铁站集合,约定等到相互见面的时候就发一条朋友圈。

import java.util.concurrent.BrokenBarrierException;import java.util.concurrent.CyclicBarrier;public class Test {    public static void main(String[] args) {        final CyclicBarrier CyclicBarrier = new CyclicBarrier(2);        for (int i = 0; i < 2; i++) {            new Thread(() -> {                String name = Thread.currentThread().getName();                if (name.equals("Thread-0")) {                    name = "3y";                } else {                    name = "女朋友";                }                System.out.println(name + "到了体育西");                try {                    // 两个人都要到体育西才能发朋友圈                    CyclicBarrier.await();                    // 他俩到达了体育西,看见了对方发了一条朋友圈:                    System.out.println("跟" + name + "去夜上海吃东西~");                } catch (InterruptedException e) {                    e.printStackTrace();                } catch (BrokenBarrierException e) {                    e.printStackTrace();                }            }).start();        }    }}

测试结果:

164d942fdd7161dc?w=915&h=202&f=png&s=12542

玩了一天以后,各自回到家里,3y和女朋友约定各自洗澡完之后再聊天

import java.util.concurrent.BrokenBarrierException;import java.util.concurrent.CyclicBarrier;public class Test {    public static void main(String[] args) {        final CyclicBarrier CyclicBarrier = new CyclicBarrier(2);        for (int i = 0; i < 2; i++) {            new Thread(() -> {                String name = Thread.currentThread().getName();                if (name.equals("Thread-0")) {                    name = "3y";                } else {                    name = "女朋友";                }                System.out.println(name + "到了体育西");                try {                    // 两个人都要到体育西才能发朋友圈                    CyclicBarrier.await();                    // 他俩到达了体育西,看见了对方发了一条朋友圈:                    System.out.println("跟" + name + "去夜上海吃东西~");                    // 回家                    CyclicBarrier.await();                    System.out.println(name + "洗澡");                    // 洗澡完之后一起聊天                    CyclicBarrier.await();                    System.out.println("一起聊天");                } catch (InterruptedException e) {                    e.printStackTrace();                } catch (BrokenBarrierException e) {                    e.printStackTrace();                }            }).start();        }    }}

测试结果:

164d942fd928d454?w=1010&h=330&f=png&s=23058

参考资料:

三、Semaphore

3.1Semaphore简介

Semaphores are often used to restrict the number of threads than can access some (physical or logical) resource.


  • A counting semaphore. Conceptually, a semaphore maintains a set of permits. Each {@link #acquire} blocks if necessary until a permit is available, and then takes it. Each {@link #release} adds a permit,potentially releasing a blocking acquirer.However, no actual permit objects are used; the {@code Semaphore} just
    keeps a count of the number available and acts accordingly.

Semaphore(信号量)实际上就是可以控制同时访问的线程个数,它维护了一组"许可证"

  • 当调用acquire()方法时,会消费一个许可证。如果没有许可证了,会阻塞起来
  • 当调用release()方法时,会添加一个许可证。
  • 这些"许可证"的个数其实就是一个count变量罢了~

3.2Semaphore例子

3y女朋友开了一间卖酸奶的小店,小店一次只能容纳5个顾客挑选购买,超过5个就需要排队啦~~~

import java.util.concurrent.Semaphore;public class Test {    public static void main(String[] args) {        // 假设有50个同时来到酸奶店门口        int nums = 50;        // 酸奶店只能容纳10个人同时挑选酸奶        Semaphore semaphore = new Semaphore(10);        for (int i = 0; i < nums; i++) {            int finalI = i;            new Thread(() -> {                try {                    // 有"号"的才能进酸奶店挑选购买                    semaphore.acquire();                    System.out.println("顾客" + finalI + "在挑选商品,购买...");                    // 假设挑选了xx长时间,购买了                    Thread.sleep(1000);                    // 归还一个许可,后边的就可以进来购买了                    System.out.println("顾客" + finalI + "购买完毕了...");                    semaphore.release();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }).start();        }    }}

输出结果:

164d942fdb70a172?w=1147&h=620&f=png&s=48679

反正每次只能5个客户同时进酸奶小店购买挑选。

参考资料:

四、总结

Java为我们提供了三个同步工具类

  • CountDownLatch(闭锁)
    • 某个线程等待其他线程执行完毕后,它才执行(其他线程等待某个线程执行完毕后,它才执行)
  • CyclicBarrier(栅栏)
    • 一组线程互相等待至某个状态,这组线程再同时执行。
  • Semaphore(信号量)
    • 控制一组线程同时执行

本文简单的介绍了一下这三个同步工具类是干嘛用的,要深入还得看源码或者借鉴其他的资料。

最后补充一下之前的思维导图知识点:

164d94300b13d901?w=3248&h=3086&f=jpeg&s=804382

参考资料:

  • 《Java并发编程实战》

如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y。为了大家方便,刚新建了一下qq群:742919422,大家也可以去交流交流。谢谢支持了!希望能多介绍给其他有需要的朋友

文章的目录导航

转载于:https://www.cnblogs.com/Java3y/p/9375669.html

你可能感兴趣的文章
WCF揭秘——使用AJAX+WCF服务进行页面开发
查看>>
【题解】青蛙的约会
查看>>
mybatis调用存储过程,获取返回的游标
查看>>
设计模式之装饰模式(结构型)
查看>>
Swift3.0服务端开发(三) Mustache页面模板与日志记录
查看>>
EntityFrameWork 实现实体类和DBContext分离在不同类库
查看>>
autopep8
查看>>
GIT在Linux上的安装和使用简介
查看>>
[转]: 视图和表的区别和联系
查看>>
Android 官方新手指导教程
查看>>
幸运转盘v1.0 【附视频】我的Android原创处女作,请支持!
查看>>
[51nod] 1199 Money out of Thin Air #线段树+DFS序
查看>>
Red and Black(poj-1979)
查看>>
安装 Express
查看>>
NOIP2013 提高组 Day1
查看>>
cocos2dx 3.x simpleAudioEngine 长音效被众多短音效打断问题
查看>>
存储(硬件方面的一些基本术语)
查看>>
观察者模式
查看>>
Weka中数据挖掘与机器学习系列之基本概念(三)
查看>>
Win磁盘MBR转换为GUID
查看>>