Semaphore信号量

简介

它本质上是一个共享锁,限制访问公共资源的线程数目,它也被称为计数信号量
acquire()许可一个线程, Semaphore – 1; 没有可用的许可时,Semaphore=0 ,线程阻塞
release()释放一个线程, Semaphore + 1

示例

public class MySemaphore {
public static void main(String[] args) {
// 使用线程池
ExecutorService exec = Executors.newCachedThreadPool();
// 只允许3个线程同时访问
final Semaphore semp = new Semaphore(3); // 模拟4个客户端访问
for (int index = 0; index < 4; index++) { Runnable run = new Runnable() {
public void run() {
try {
// 获取许可
semp.acquire(); System.out.println("线程"+ Thread.currentThread().getName() + "获得许可:"); // 模拟耗时的任务
for (int i = 0; i < 999999; i++); // 释放许可
semp.release(); System.out.println("线程"+ Thread.currentThread().getName() + "释放许可:");
System.out.println("当前允许进入的任务个数:"+ semp.availablePermits()); } catch (InterruptedException e) {
e.printStackTrace();
}
}
}; exec.execute(run);
}
// 关闭线程池
exec.shutdown();
}
}

控制台输出:

线程pool-1-thread-1获得许可:
线程pool-1-thread-2获得许可:
线程pool-1-thread-2释放许可:
当前允许进入的任务个数:2 //总共允许3个许可, 获取两个许可, 释放一个许可, 剩余2个许可
线程pool-1-thread-1释放许可:
当前允许进入的任务个数:2 //释放一个许可, 应该打印出1, 可以看出, Semaphore并不保证线程安全
线程pool-1-thread-3获得许可:
线程pool-1-thread-3释放许可:
当前允许进入的任务个数:2
线程pool-1-thread-4获得许可:
线程pool-1-thread-4释放许可:
当前允许进入的任务个数:3

CyclicBarrier 障碍器

简介

允许一组线程互相等待,到达一个公共的障碍点, 该组任务完成后, 再去完成另外一个任务
在释放等待线程后可以重用,它是循环的barrier

示例

public class MyCyclicBarrier {
public static void main(String[] args) {
//创建CyclicBarrier对象, 并设置执行完一组5个线程的并发任务后,再执行MainTask任务
CyclicBarrier cb = new CyclicBarrier(5, new MainTask()); new SubTask("A", cb).start();
new SubTask("B", cb).start();
new SubTask("C", cb).start();
new SubTask("D", cb).start();
new SubTask("E", cb).start();
}
} /** 最后执行的任务 */
class MainTask implements Runnable {
public void run() {
System.out.println("......终于要执行最后的任务了......");
}
} /** 一组并发任务 */
class SubTask extends Thread {
private String name;
private CyclicBarrier cb; SubTask(String name, CyclicBarrier cb) {
this.name = name;
this.cb = cb;
} public void run() {
System.out.println("[并发任务" + name + "] 开始执行"); for (int i = 0; i < 999999; i++); // 模拟耗时的任务 System.out.println("[并发任务" + name + "] 执行完毕,通知障碍器");
try {
// 每执行完一项任务就通知障碍器
cb.await(); } catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}

控制台输出:

[并发任务A]  开始执行
[并发任务D] 开始执行
[并发任务C] 开始执行
[并发任务B] 开始执行
[并发任务E] 开始执行
[并发任务B] 执行完毕,通知障碍器
[并发任务E] 执行完毕,通知障碍器
[并发任务D] 执行完毕,通知障碍器
[并发任务A] 执行完毕,通知障碍器
[并发任务C] 执行完毕,通知障碍器
......终于要执行最后的任务了...... //可以看出执行一组任务后,在执行这个线程任务

CountDownLatch 障碍器 

简介

允许1或N个线程等待其他线程完成后在执行
调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置

示例

public class MyCountDownLatch {
public static void main(String[] args) {
//启动会议室线程,等待与会人员参加会议
Conference conference = new Conference(3);
new Thread(conference).start(); //参会者线程
for(int i = 0 ; i < 3 ; i++){
Participater participater = new Participater("" + i , conference);
Thread thread = new Thread(participater);
thread.start();
}
}
} /** 会场类 */
class Conference implements Runnable{
private final CountDownLatch countDown;//障碍器 public Conference(int count){
countDown = new CountDownLatch(count);
} /** 与会人员到达 */
public void arrive(String name){
System.out.println(name + "到达....."); //到达一个,锁计数器 - 1, 在计数到达0之前会一直阻塞
countDown.countDown(); System.out.println("还有 " + countDown.getCount() + "位没有到达...");
} @Override
public void run() {
System.out.println("准备开会,参加会议人员总数为:" + countDown.getCount()); //调用await(),等待所有的与会人员到达
try {
countDown.await();
} catch (InterruptedException e) {
} System.out.println("所有人员已经到达,会议开始.....");
}
} /** 参会者类*/
class Participater implements Runnable{
private String name;
private Conference conference; public Participater(String name,Conference conference){
this.name = name;
this.conference = conference;
} @Override
public void run() {
conference.arrive(name);
}
}

控制台输出:

准备开会,参加会议人员总数为:3
2到达.....
还有 2位没有到达...
0到达.....
还有 1位没有到达...
1到达.....
所有人员已经到达,会议开始.....
还有 0位没有到达...

Phaser

简介

推荐阅读: http://whitesock.iteye.com/blog/1135457

http://www.2cto.com/kf/201611/560952.html

任务数目是可变的: 可以在任何时间注册新的参与者;并且在抵达屏障点时,可以注销已经注册的参与者

phase和party

phase就是阶段,初值为0:

当所有的线程执行完本轮任务,同时开始下一轮任务时,意味着当前阶段已结束,
进入到下一阶段,phase的值自动加1

party就是线程:  party=4就意味着Phaser对象当前管理着4个线程

boolean onAdvance(int phase, int registeredParties) :

1.当此方法返回true时,意味着Phaser被终止, 若此方法返回值为 phase>=3,其含义为当整个线程执行了4个阶段后,程序终止

2.当每一个阶段执行完毕,此方法会被自动调用 ,此方法内的代码会在每个阶段执行完毕时执行

示例: 可变数目的任务

import java.util.Random;
import java.util.concurrent.Phaser;
import java.util.concurrent.atomic.AtomicInteger; /**
*可变数目: 动态注册和取消
*
*示例:
* 在旅游过程中,有可能很凑巧遇到几个朋友,
* 然后他们听说你们在旅游,所以想要加入一起继续接下来的旅游.
* 也有可能,在旅游过程中,突然其中有某几个人临时有事,想退出这次旅游了
*/
public class MyPhaser_5 {
public static void main(String[] args) {
final int num = 3;
Phaser phaser = new Phaser(num){
/**
* 如果该方法返回true,那么Phaser会被终止, 默认实现是在注册任务数为0时返回true
* phase : 阶段数
* registeredParties : 注册的线程数
*/
@Override
protected boolean onAdvance(int phase, int registeredParties) {
System.out.println("" + getArrivedParties() + "个人都到齐了,第" + (phase + 1) + "次集合 \n");
return phase >= num;
}
}; new Thread(new TourismRunnable(phaser),"小明").start();
new Thread(new TourismRunnable(phaser),"小刚").start();
new Thread(new TourismRunnable(phaser),"小红").start();
}
} /** 旅行线程 */
class TourismRunnable implements Runnable{
Phaser phaser;
/**
* 每个线程保存一个朋友计数器,小红第一次遇到一个朋友,取名`小红的朋友0号`,第二次遇到一个朋友,取名为`小红的朋友1号`
*/
AtomicInteger frientCount = new AtomicInteger(); public TourismRunnable(Phaser phaser) {
this.phaser = phaser;
} @Override
public void run() {
switch (phaser.getPhase()){
case 0:if(!goToPoint("出发点")) break;
case 1:if(!goToPoint("旅游景点")) break;
case 2:if(!goToPoint("酒店")) break;
}
} /**
* @param point 目的地
* @return 返回true,说明还要继续旅游,否则就临时退出了
*/
private boolean goToPoint(String point){
try {
if(!randomEvent()){
//取消注册
phaser.arriveAndDeregister();
return false;
}
System.out.println(Thread.currentThread().getName() + "到了" + point); //阻塞
phaser.arriveAndAwaitAdvance();
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
} /**
* 随机事件: 遇到新朋友一起旅游 或者 中途退出旅游
* @return 返回true,说明还要继续旅游,否则就临时退出了
*/
private boolean randomEvent() {
int random = new Random().nextInt(100);
String name = Thread.currentThread().getName(); if (random < 10){
int friendNum = 1;
System.out.println("=====================" + name + ":遇到了"+friendNum+"个朋友,要一起去旅游"); new Thread(new TourismRunnable(phaser), name + "的朋友" + frientCount.incrementAndGet() + "号").start();
//注册
phaser.bulkRegister(friendNum); }else if(random > 80){
System.out.println("=====================" + name + ":突然有事要离开一下,不和他们继续旅游了");
return false;
} return true;
}
}

Java并发编程--5.信号量和障碍器的更多相关文章

  1. Java并发编程-JUC-CountDownLatch 倒计数门闩器-等待多线程完成再放行 -一次性使用

    如题 (总结要点) CountDownLatch 倒计数门闩器, 让1-n-1个线程等待其他多线程完成工作. (Excel的多个Sheet的解析,最终等待解析完毕后;要实现主线程等待所有线程完成she ...

  2. [java并发编程]基于信号量semaphore实现限流器

    目录 一.什么是信号量 二.信号量类Semaphore 三.实现限流器 欢迎关注我的博客,更多精品知识合集 一.什么是信号量 "信号量"在编程术语中使用单词semaphore,那什 ...

  3. 【Java并发编程五】信号量

    一.概述 技术信号量用来控制能够同时访问某特定资源的活动的数量,或者同时执行某一给定操作的数据.计数信号量可以用来实现资源池或者给一个容器限定边界. 信号量维护了一个许可集,许可的初始量通过构造函数传 ...

  4. 【Java并发编程】并发编程大合集-值得收藏

    http://blog.csdn.net/ns_code/article/details/17539599这个博主的关于java并发编程系列很不错,值得收藏. 为了方便各位网友学习以及方便自己复习之用 ...

  5. 【Java并发编程】并发编程大合集

    转载自:http://blog.csdn.net/ns_code/article/details/17539599 为了方便各位网友学习以及方便自己复习之用,将Java并发编程系列内容系列内容按照由浅 ...

  6. Java并发编程实现概览

    并发概览 >>同步 如何同步多个线程对共享资源的访问是多线程编程中最基本的问题之一.当多个线程并发访问共享数据时会出现数据处于计算中间状态或者不一致的问题,从而影响到程序的正确运行.我们通 ...

  7. Java并发编程二三事

    Java并发编程二三事 转自我的Github 近日重新翻了一下<Java Concurrency in Practice>故以此文记之. 我觉得Java的并发可以从下面三个点去理解: * ...

  8. Java并发编程深入学习

    上周的面试中,被问及了几个并发开发的问题,自己回答的都不是很系统和全面,可以说是"头皮发麻",哈哈.因此果断购入<Java并发编程的艺术>一书,该书内容主要是对ifev ...

  9. 《Java并发编程实战》/童云兰译【PDF】下载

    <Java并发编程实战>/童云兰译[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062521 内容简介 本书深入浅出地介绍了Jav ...

随机推荐

  1. oracle中scott用户的创建

    原创作品,转载请在文章开头显眼位置注明出处:https://www.cnblogs.com/sunshine5683/p/10046716.html 今天,接着上次的学习进度继续前进,在此过程中,使用 ...

  2. Docker网络实践运用

    Docker 容器网络概述 要构建具有安全的一致行为的 Web 应用程序,可以使用 Docker 网络特性.根据定义,网络为容器实现了完全隔离.因此,控制应用程序所在的网络很重要.Docker 容器网 ...

  3. C#动态创建lambda表达式

    /// <summary> /// 创建lambda表达式:p=>true /// </summary> /// <typeparam name="T&q ...

  4. 适配器(GOF23)

    ---恢复内容开始--- 摘要:由于应用环境的变化,需要将现存的对象放到新的环境中去,但新环境的接口是现存对象不满足的. 意图:将原本接口不兼容的类通过转换,使得它们能够一起工作,复用现有的类 ada ...

  5. mac 下mongodb connect failed 连接错误

    我是用brew install mongod 安装的 MongoDB shell version v3.4.2connecting to: mongodb://127.0.0.1:270172017- ...

  6. opencv3.2.0实现视频抽帧,并保存成图片

    .实现指定帧数的抽取.和全部帧数的抽取,并保存到指定目录. 在QT新建一个控制台程序,程序源码如下:(程序实现每十帧获取一次帧) #include <QCoreApplication> # ...

  7. 网络基础 Windows下安装和配置net-snmp 代理

    Windows 下安装和配置net-snmp 代理[摘录] by:授客 QQ:1033553122   A.   安装  1.   安装前准备 ActivePerl-5.10.0.1004-MSWin ...

  8. js 中文长字符截短&关键字符隐藏 自定义过滤器

    两个非常简单的过滤器:隐藏关键字符和字符截短.同样也可以迁移到ng和原生js直接使用(去掉avalon.filters声明即可).后期还有不错的过滤器,还往这里面加 keyword:avalon,js ...

  9. Week5——Ajax

    1.简介 AJAX 相当于异步 JavaScript 和 XML,是一种用于创建快速动态网页的技术.通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新.这意味着可以在不重新加载整个网 ...

  10. map合并,相同键对应的值相加

    最近在做统计钱的计算时遇到的一个需求,需要将一个大类别下的每一种钱进行特定的运算然后获得六年的钱,最后将这些钱按照年份进行汇总,获得总得大类型的六年的钱,在这个过程中采用了这种方法,每次算得钱放在ma ...