https://blog.youkuaiyun.com/qq_28822933/article/details/83340642
CountDownLatch(闭锁,我觉得叫门闩更好理解)
CyclicBarrier(栅栏)
Semophore(信号量)
Exchanger(交换器
(一)CountDownLatch
CountDownLatch是一个同步计数器,初始化的时候传入需要计数线程的等待数,可能是等待执行完的线程数,或者大于;调用多个线程之间的同步,或者说起到线程之间的通信(不是互斥)一组线程等待其他线程完成工作之后在执行,相当于加强的join
await();阻塞当前线程,知道计数器数值减到0
countdown();负责计数器减1
例子:主线程等待其他线程执行完后之后再执行
package bingfalei;
/*
* 测试CountDownLatch并发工具类
* 主线程等待其他线程执行完后之后再执行
* */
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
private static CountDownLatch countDownLatch = new CountDownLatch(6);
private static class BusiThread extends Thread{
public void run(){
try {
System.out.println("线程"+Thread.currentThread().getName()+"准备运行");
sleep(100);
System.out.println("线程"+Thread.currentThread().getName()+"运行完成");
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
System.out.println("aaa线程"+Thread.currentThread().getName()+"开始初始化");
countDownLatch.countDown();
}
},"main-2").start();;
for(int i =0;i<5;i++){
new BusiThread().start();
}
try {
countDownLatch.await();
System.out.println("主程序执行结束");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
----------------------------------------------------------分割线,结果-------------------------------------------------------------------------------------
aaa线程main-2开始初始化
线程Thread-0准备运行
线程Thread-1准备运行
线程Thread-2准备运行
线程Thread-3准备运行
线程Thread-4准备运行
线程Thread-0运行完成
线程Thread-1运行完成
线程Thread-2运行完成
线程Thread-3运行完成
线程Thread-4运行完成
主程序执行结束
(二)CyclicBarrier
CyclicBarrier字面意思就是栅栏,是多线程中的重要的类,主要用于线程之间互相等待的问题,初始化的时候传入需要等待的线程数作用:让一组线程达到某个屏障被阻塞直到一组内最后一个线程达到屏蔽时,屏蔽开放,所有被阻塞的线程才会继续运行其中:
CyclicBarrier(int parties);初始化需要等待的线程数parties
CyclicBarrier(int parties,Runnable barrierAction);当屏蔽开放时,线程barrierAction的任务会执行
CountDownLatch和CyclicBarrier
1.CountDownLatch释放由第三者控制,CyclicBarrier释放由一组线程本身控制
2.CountDownLatch释放条件》=线程数,而CyclicBarrier释放条件=线程数
3.CountDownLatch会阻塞主线程,CyclicBarrier不会阻塞主线程,只会阻塞子线程
4.CountDownLatch的计数器只能把使用一次,而CyclicBarrier的计数器可以用reset重置
综上,CyclicBarrier能处理更为复杂的业务场景,比如计算发生错误,可以重置计数器,并让线程重新执行一次
测试:五个子线程一起完成
package bingfalei;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
private static CyclicBarrier barrier = new CyclicBarrier(5,new CollectThread());
private static ConcurrentHashMap<Long,String> concurrentHashMap = new ConcurrentHashMap<Long,String>();
private static class CollectThread extends Thread{
@Override
public void run() {
System.out.println(concurrentHashMap.size());
}
}
private static class SubThread extends Thread{
@Override
public void run() {
Random random = new Random();
long id =Thread.currentThread().getId();
String name = Thread.currentThread().getName();
concurrentHashMap.put(id, name);
try {
System.out.println("线程"+name+"处理结束,等待其他线程处理结束");
sleep(2000);
barrier.await();
System.out.println("处理完毕");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
for(int i =0;i<5;i++){
new SubThread().start();
}
System.out.println("主线程");
}
}
----------------------------------------------------------分割线,结果-------------------------------------------------------------------------------------
主线程
线程Thread-2处理结束,等待其他线程处理结束
线程Thread-5处理结束,等待其他线程处理结束
线程Thread-1处理结束,等待其他线程处理结束
线程Thread-4处理结束,等待其他线程处理结束
线程Thread-3处理结束,等待其他线程处理结束
5
处理完毕
处理完毕
处理完毕
处理完毕
处理完毕
(三)Semaphore
semaphore又名信号量,是操作系统的一个概念,在Java并发编程中,信号量控制的是线程并发的数量
作用:semaphore管理一系列许可每个acquire()方法阻塞,直到有一个许可证可以获得,然后拿走一个许可证,每个release方法增加一个许可证,这可能会释放一个阻塞的acquire方法,然而并没有实际的许可保证这个对象,semaphore只是维持了一个可获取许可的数量,主要控制同时访问某个特定资源的线程数量,多用在流量控制
其他semaphore的底层实现就是基于AQS的共享锁实现的
如果一个线程要访问共享资源,必须先获得信号量,如果信号量的计数器数值大于1,意味着有共享资源可以访问,使其计数器减1,再访问共享资源,如果计数器为0,线程进入休眠,当某个线程使用完共享资源后,释放信号量,并将信号量的内部计数器加1,之前进入休眠的线程将被唤醒并再次试图获取信号量
例:用信号量模拟一个容量为10的线程池;连接50个线程处理事务
package bingfalei;
//模拟数据库连接实现
public class Connectionimpl implements Connection {
public static Connection getCon(){
return new Connectionimpl();
}
//省略若干重写方法
}
package bingfalei;
/*
* 模拟数据库的连接操作,其中重写了一个方法,休息10ms,模拟实际操作耗时
* */
import java.sql.Connection;
import java.util.LinkedList;
import java.util.concurrent.Semaphore;
public class SemaphoreDemo {
//线程池
private static LinkedList<Connection> list = new LinkedList<Connection>();
//线程池的大小
private static final int POOL_SIZE = 10;
//useful:可使用的数据库连接数 useless 不可使用的数据库连接数
private Semaphore useful,useless;
public SemaphoreDemo() {
useful = new Semaphore(POOL_SIZE);
useless = new Semaphore(0);
}
//初始化线程池
static {
for(int i=0;i<POOL_SIZE;i++){
list.add(new Connectionimpl());
}
}
//获取数据库连接
public Connection getConnection(){
Connection con =null;
try {
//可用信号量获取许可,阻塞方法,只有当前线程获取到许可证才能放下行
useful.acquire();
synchronized (list) {
con = list.removeFirst();
}
useless.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
return con;
}
//释放连接
public void releaseConnection(Connection con){
if(con != null){
System.out.println("当前有"+useful.getQueueLength()+"个线程等待数据库连接"+"可以连接数:"+useful.availablePermits());
try {
//不可用信号量的获取
useless.acquire();
//放回池
synchronized (list) {
list.addLast(con);
}
useful.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package bingfalei;
import java.sql.Connection;
import java.util.Random;
public class TestDemo {
private static SemaphoreDemo demo = new SemaphoreDemo();
private static class BusiThread extends Thread{
long start = System.currentTimeMillis();
public void run() {
Random r = new Random();//让每个线程连接时间不同
Connection con = demo.getConnection();
System.out.println("Thread_"+Thread.currentThread().getName()+"_获取数据库连接共耗时:"+(System.currentTimeMillis()-start)+"ms....");
//模拟业务操作,线程持有查询连接数据
System.out.println("查询数据完成,归还连接");
demo.releaseConnection(con);
}
}
public static void main(String[] args) {
for(int i =0;i<50;i++){
new BusiThread().start();
}
}
}
4.Exchange
Exchange类似于一个交换器可以在队中元素进行配对和交换的线程的同步点,用于两个线程之间的交换
具体来说,Exchanger类允许两个线程之间定义同步点当两个线程达到同步点时,他们交换数据结构,因此第一个线程的数据结构进入到第二个线程当中,第二个线程的数据结构进入到第一个线程当中。
例:交换两个线程各自的数据//好象失败了
package bingfalei;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Exchanger;
public class ExchangeDemo {
private static Exchanger<Set<String>> exchanger = new Exchanger<Set<String>>();
private static class ExchangerClassO extends Thread{
private Set<String> set;
public ExchangerClassO( Set<String> set){
this.set=set;
}
@Override
public void run() {
try {
exchange(set);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private static class ExchangerClassT extends Thread{
private Set<String> set;
public ExchangerClassT( Set<String> set){
this.set=set;
}
@Override
public void run() {
try {
exchange(set);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private static void exchange(Set<String> set) throws InterruptedException{
//交换数据,阻塞
// System.out.println("线程"+Thread.currentThread().getName()+"交换前值");
// for(String s:set){
// System.out.println("当前线程"+Thread.currentThread().getName()+s);
// }
exchanger.exchange(set);
System.out.println("线程"+Thread.currentThread().getName()+"交换后值");
for(String s:set){
System.out.println("当前线程"+Thread.currentThread().getName()+s);
}
}
public static void main(String[] args) {
Set<String> setA = new HashSet<String>();
Set<String> setB = new HashSet<String>();
setA.add("a1");
setA.add("b1");
setA.add("c1");
setB.add("a2");
setB.add("b2");
setB.add("c2");
Thread t1 = new ExchangerClassO(setA);
t1.start();
// try {
// Thread.sleep(3000);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
new ExchangerClassT(setB).start();
}
}