java 5线程中 Semaphore信号灯,CyclicBarrier类,CountDownLatch计数器以及Exchanger类使用
先来讲解一下Semaphore信号灯的作用:
可以维护当前访问自身的线程个数,并提供了同步机制,
使用semaphore可以控制同时访问资源的线程个数
例如,实现一个文件允许的并发访问数。
请看下面的演示代码:
public class SemaphoreTest
{
public static void main(String[] args)
{
//创建一个带有缓存的线程池
ExecutorService service = Executors.newCachedThreadPool();
//创建三个信号灯
final Semaphore sp = new Semaphore(3);//最多并发三个线程 此处可以按照需求去修改
//开启十个线程
for (int i = 1; i <= 10; i++)
{
//只有三个线程可以同时进入 其余线程等待
service.execute(new Runnable()
{
@Override
public void run()
{
try
{
sp.acquire();//获取一盏信号灯
} catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("线程 "+Thread.currentThread().getName()+" 进入"
+ " ,当前已有 "+(3-sp.availablePermits())+ " 个并发");
try
{
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("线程 "+Thread.currentThread().getName()+" 即将离开 ");
sp.release();//释放
System.out.println("线程 "+Thread.currentThread().getName()+" 已经离开"
+ " ,当前已有 "+(3-sp.availablePermits())+ " 个并发");
}
});
}
service.shutdown();
}
}
执行结果如下:
线程 pool-1-thread-2 进入 ,当前已有 2 个并发
线程 pool-1-thread-3 进入 ,当前已有 3 个并发
线程 pool-1-thread-1 进入 ,当前已有 3 个并发
线程 pool-1-thread-3 即将离开
线程 pool-1-thread-3 已经离开 ,当前已有 2 个并发
线程 pool-1-thread-4 进入 ,当前已有 3 个并发
线程 pool-1-thread-1 即将离开
线程 pool-1-thread-5 进入 ,当前已有 3 个并发
线程 pool-1-thread-1 已经离开 ,当前已有 3 个并发
线程 pool-1-thread-2 即将离开
线程 pool-1-thread-2 已经离开 ,当前已有 2 个并发
线程 pool-1-thread-6 进入 ,当前已有 3 个并发
线程 pool-1-thread-6 即将离开
线程 pool-1-thread-6 已经离开 ,当前已有 2 个并发
线程 pool-1-thread-7 进入 ,当前已有 3 个并发
线程 pool-1-thread-7 即将离开
线程 pool-1-thread-7 已经离开 ,当前已有 2 个并发
线程 pool-1-thread-8 进入 ,当前已有 3 个并发
线程 pool-1-thread-4 即将离开
线程 pool-1-thread-4 已经离开 ,当前已有 2 个并发
线程 pool-1-thread-9 进入 ,当前已有 3 个并发
线程 pool-1-thread-5 即将离开
线程 pool-1-thread-5 已经离开 ,当前已有 2 个并发
线程 pool-1-thread-10 进入 ,当前已有 3 个并发
线程 pool-1-thread-10 即将离开
线程 pool-1-thread-10 已经离开 ,当前已有 2 个并发
线程 pool-1-thread-8 即将离开
线程 pool-1-thread-8 已经离开 ,当前已有 1 个并发
线程 pool-1-thread-9 即将离开
线程 pool-1-thread-9 已经离开 ,当前已有 0 个并发
Semaphore信号灯可以控制并发数,保证每次最多只能有三个线程在线程池中。
CyclicBarrier类的使用,可以模拟现实生活中的多人等待上车的情形,例如多人去旅行,那么当A到达集合点时,不能立即出发,必须等到B也到达集合点,那么A和B必须等到C也到达集合点,此时,三人可以坐车出发去下一站。该类就可以实现此功能,请看如下代码。
public class CyclicBarrierTest
{
public static void main(String[] args)
{
//创建一个带有缓存的线程池
ExecutorService service = Executors.newCachedThreadPool();
//指定三个线程 只有当三个线程同时到达时 程序才会往下执行
final CyclicBarrier cb = new CyclicBarrier(3); for (int i = 0; i < 3; i++)
{
Runnable runnable = new Runnable()
{
@Override
public void run()
{
try
{
/**
* cb.getNumberWaiting():从0开始,获取当前等待的线程数量
*/
//第一个
Thread.sleep(new Random().nextInt(1000));
System.out.println("线程 "+Thread.currentThread().getName()+" 即将到达集合地点1,"
+ "当前已有"+(cb.getNumberWaiting()+1)+" 个线程,"+(cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在继续等待"));
cb.await();//让线程等待 //第二个
Thread.sleep(new Random().nextInt(1000));
System.out.println("线程 "+Thread.currentThread().getName()+" 即将到达集合地点2,"
+ "当前已有"+(cb.getNumberWaiting()+1)+" 个线程,"+(cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在继续等待"));
cb.await(); //第三个
Thread.sleep(new Random().nextInt(1000));
System.out.println("线程 "+Thread.currentThread().getName()+" 即将到达集合地点3,"
+ "当前已有"+(cb.getNumberWaiting()+1)+" 个线程,"+(cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在继续等待"));
cb.await();
} catch (InterruptedException | BrokenBarrierException e)
{
e.printStackTrace();
}
}
};
service.execute(runnable);
} service.shutdown();//关闭线程池
}
}
如下是执行结果:
线程 pool-1-thread-2 即将到达集合地点1,当前已有1 个线程,正在继续等待
线程 pool-1-thread-1 即将到达集合地点1,当前已有2 个线程,正在继续等待
线程 pool-1-thread-3 即将到达集合地点1,当前已有3 个线程,都到齐了,继续走啊
线程 pool-1-thread-2 即将到达集合地点2,当前已有1 个线程,正在继续等待
线程 pool-1-thread-1 即将到达集合地点2,当前已有2 个线程,正在继续等待
线程 pool-1-thread-3 即将到达集合地点2,当前已有3 个线程,都到齐了,继续走啊
线程 pool-1-thread-1 即将到达集合地点3,当前已有1 个线程,正在继续等待
线程 pool-1-thread-2 即将到达集合地点3,当前已有2 个线程,正在继续等待
线程 pool-1-thread-3 即将到达集合地点3,当前已有3 个线程,都到齐了,继续走啊
CountDownLatch计数器的使用:
* 演示一个计数器CountDownLatch
* 模拟百米赛跑
* 1个裁判 吹口哨
* 3个运动员
public class CountDownLatchTest
{
public static void main(String[] args)
{
//创建一个带有缓存的线程池
ExecutorService service = Executors.newCachedThreadPool();
final CountDownLatch cdOrder = new CountDownLatch(1);//裁判
final CountDownLatch cdAnswer = new CountDownLatch(3);//运动员
for (int i = 0; i < 3; i++)
{
Runnable runnable = new Runnable()
{
@Override
public void run()
{
try
{
System.out.println("线程"+Thread.currentThread().getName()+" 正准备接收命令 ");
cdOrder.await();//等待计数器归0时 代码向下走
System.out.println("线程"+Thread.currentThread().getName()+" 已接收命令 ");
Thread.sleep(new Random().nextInt(1000));
System.out.println("线程"+Thread.currentThread().getName()+" 回应命令处理结果 ");
cdAnswer.countDown();//没调用一次该方法 就会将当前计数器上的计数减1
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
};
service.execute(runnable);
} //主线恒
try
{
Thread.sleep(new Random().nextInt(1000));
System.out.println("线程"+Thread.currentThread().getName()+" 即将发布命令 ");
cdOrder.countDown();//相当于把计数器身上的计数减1
System.out.println("线程"+Thread.currentThread().getName()+" 已发送命令,正在等待结果 ");
cdAnswer.await();
System.out.println("线程"+Thread.currentThread().getName()+" 已收到所有相应结果");
} catch (InterruptedException e)
{
e.printStackTrace();
}
service.shutdown();//关闭线程池
}
}
如下是执行结果:
线程pool-1-thread-1 正准备接收命令
线程pool-1-thread-2 正准备接收命令
线程pool-1-thread-3 正准备接收命令
线程main 即将发布命令
线程main 已发送命令,正在等待结果
线程pool-1-thread-1 已接收命令
线程pool-1-thread-2 已接收命令
线程pool-1-thread-3 已接收命令
线程pool-1-thread-2 回应命令处理结果
线程pool-1-thread-3 回应命令处理结果
线程pool-1-thread-1 回应命令处理结果
线程main 已收到所有相应结果
Exchanger类可以实现两个线程之间的数据交换:
public class ExchangerTest
{
public static void main(String[] args)
{
//创建一个带有缓存的线程池
ExecutorService service = Executors.newCachedThreadPool();
final Exchanger<String> changer = new Exchanger<String>();
//开启第一个任务
service.execute(new Runnable()
{
@Override
public void run()
{
try
{
String dtail1 = "zhangsan";//准备要交换出去的数据
System.out.println("线程 "+Thread.currentThread().getName()+" 正要把"+dtail1+"换出去");
Thread.sleep(new Random().nextInt(1000));
String dtail2 = changer.exchange(dtail1);//换回来的数据
System.out.println("线程 "+Thread.currentThread().getName()+"换回的数据为"+dtail2);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}); //开启第二个任务
service.execute(new Runnable()
{
@Override
public void run()
{
try
{
String dtail1 = "lisi";//准备要交换出去的数据
System.out.println("线程 "+Thread.currentThread().getName()+" 正要把"+dtail1+"换出去");
Thread.sleep(new Random().nextInt(1000));
String dtail2 = changer.exchange(dtail1);//换回来的数据
System.out.println("线程 "+Thread.currentThread().getName()+"换回的数据为"+dtail2);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
});
service.shutdown();
}
}
如下是执行结果:
线程 pool-1-thread-1 正要把zhangsan换出去
线程 pool-1-thread-2 正要把lisi换出去
线程 pool-1-thread-1换回的数据为lisi
线程 pool-1-thread-2换回的数据为zhangsan
以上都是java 5中的一些知识点,大家可以根据实际工作中的需要进行选择使用!!
java 5线程中 Semaphore信号灯,CyclicBarrier类,CountDownLatch计数器以及Exchanger类使用的更多相关文章
- Android进阶——多线程系列之Semaphore、CyclicBarrier、CountDownLatch
今天向大家介绍的是多线程开发中的一些辅助类,他们的作用无非就是帮助我们让多个线程按照我们想要的执行顺序来执行.如果我们按照文字来理解Semaphore.CyclicBarrier.CountDownL ...
- java的线程中的Runnable
在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继承了Thr ...
- Java子线程中的异常处理(通用)
在普通的单线程程序中,捕获异常只需要通过try ... catch ... finally ...代码块就可以了.那么,在并发情况下,比如在父线程中启动了子线程,如何正确捕获子线程中的异常,从而进行相 ...
- Java子线程中操作主线程Private级别数据
两个类分别如下: <pre name="code" class="java">package Demo2; import java.util.*; ...
- 转:Java子线程中的异常处理(通用)
引自:https://www.cnblogs.com/yangfanexp/p/7594557.html 在普通的单线程程序中,捕获异常只需要通过try ... catch ... finally . ...
- JAVA多线程提高十:同步工具CyclicBarrier与CountDownLatch
今天继续学习其它的同步工具:CyclicBarrier与CountDownLatch 一.CyclicBarrier CyclicBarrier是一个同步辅助类,它允许一组线程互相等待,直到到达某个公 ...
- JUC-JUC强大的辅助类讲解(Semaphore、CyclicBarrier、CountDownLatch)
一.CountDownLatch 减少计数 1.原理 * CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞. * 其它线程调用countDown方法会将 ...
- JAVA多线程学习十三 - 同步工具CyclicBarrier与CountDownLatch
一.CyclicBarrier CyclicBarrier是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point).在涉及一组固定大小的线程的程序 ...
- Java并发包中Semaphore的工作原理、源码分析及使用示例
1. 信号量Semaphore的介绍 我们以一个停车场运作为例来说明信号量的作用.假设停车场只有三个车位,一开始三个车位都是空的.这时如果同时来了三辆车,看门人允许其中它们进入进入,然后放下车拦.以后 ...
随机推荐
- Python实现一些常用排序算法
一些常用的排序 #系统内置排序算法#list.sort()#heapq模块 def sys_heap_sort(list): import heapq heap = [] for i in range ...
- [AH/HNOI2017]礼物
\[推推公式,即求\Sigma^{n}_{i=1} (x_{i+k}-y_i+c)^2最小,c范围为[-m, m]\] \[拆开,就是\Sigma x_i^2 + \Sigma y_i^2 + n * ...
- js实现对树深度优先遍历与广度优先遍历
深度优先与广度优先的定义 首先我们先要知道什么是深度优先什么是广度优先. 深度优先遍历是指从某个顶点出发,首先访问这个顶点,然后找出刚访问这个结点的第一个未被访问的邻结点,然后再以此邻结点为顶点,继续 ...
- python中Django 使用方法简述
Django是由Python写成的免费而且开源的Web应用框架--一堆零件的组成,可以帮助我们轻松的开发网站.这些零件都包括常用的:登录(注册,登入,登出),网站后台管理,表单,文件上传等.可以帮助我 ...
- mycat操作MySQL第一篇:全局表
1.安装mycat,点击bin下面startup_nowrap.bat启动 2.客户端连接mycat:server.xml里面的 <!--连接mycat用户名和密码.数据库--> < ...
- Vue-表单输入绑定
>>>>>>> html <div id="app" > <!-- 输入框绑定 --> <input v-m ...
- Java 小记 — Spring Boot 的实践与思考
前言 本篇随笔用于记录我在学习 Java 和构建 Spring Boot 项目过程中的一些思考,包含架构.组件和部署方式等.下文仅为概要,待闲时逐一整理为详细文档. 1. 组件 开源社区如火如荼,若在 ...
- jquery pjax 用法总结
以前我们点击a链接的时候总是会刷新整个页面并跳转到新页面,中间可以很明显的看到短暂的白屏.pjax就很好的解决了这问题. pjax的原理很简单,就是发送一个ajax请求,获取html代码,再把相关代码 ...
- redis笔记总结之redis介绍
一.Redis介绍: redis的发展历史简单的理解为因为使用类似MySql这类关系型数据库不方便进而开发的开源的.轻量级的.非关系型的,直到现在一直不断完善的一款NoSql数据库.具体的介绍大家可以 ...
- ETL总结(扫盲版)
1.ETL名词解释 英文缩写 Extract-Transform-Load ,用来描述将数据从来源端经过抽取(extract).转换(transform).加载(load)至到目的端(一般指的是数 ...