先来讲解一下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的介绍 我们以一个停车场运作为例来说明信号量的作用.假设停车场只有三个车位,一开始三个车位都是空的.这时如果同时来了三辆车,看门人允许其中它们进入进入,然后放下车拦.以后 ...
随机推荐
-
4、delphi record数组复制
SetLength(OldDeptInfo,0); //释放旧数组 OldDeptInfo:=nil; 这样也可以: //SetLength(OldDeptInfo,Length(NewDeptInf ...
-
java获取每个月的最后一天
package timeUtil; import java.text.SimpleDateFormat; import java.util.Calendar; public class LastDay ...
-
在 VC6 中使用 GdiPlus-使用
下面用 VC6 来写一个 GdiPlus 的 Demo 工程 Step1:新建一个名为 Demo_GdiPlus 的 MFC AppWizard(exe) 工程 操作步骤:(1)主菜单File-> ...
-
c#获取远程图片的方法
public static int SaveImageFromWeb(string imgUrl, string path) { var aaa = Environment.CurrentDirect ...
-
【转】android创建Popwindow弹出菜单的两种方式
方法一的Activity package com.app.test02; import android.app.Activity; import android.os.Bundle; import a ...
-
各开放平台API接口通用 SDK 前言
最近两年一直在做API接口相关的工作,在平时工作中以及网上看到很多刚接触API接口调用的新人一开始会感到很不适应,包括自己刚开始做API接口调用的相关工作时,也是比较抓狂的,所有写一序列文章把之前的工 ...
-
关于Autosar中的NM模块的理解
本篇文章主要介绍AutoSar中关于NM模块的理解. 阅读本篇文章希望达到的目的: 1. NM(网络管理)是用来做什么的: 2. AutoSar中网络管理的原理: 3.项目实例介绍 1. NM(网络管 ...
-
sublime test3 乱码问题的解决
1.下载ConvertToUTF8插件,地址:http://pan.baidu.com/s/1bnvVd2R 2.按Ctrl+Shift+P打开命令行,输入Install Package,回车,然后继 ...
-
js 执行机制
浏览器中每个一个窗口都是一个单独的进程.这就需要分析浏览器与Javascript解释引擎之间的关系.先给出结论,浏览器本身是多线程的,Javascript解释引擎是单线程的. 先说说浏览器有哪些线程, ...
-
cloudstack网络部分知识点汇总
UI界面的几个网络选项 DNS1:供此区域的来宾VM使用,此区域的公用IP必须路由到此服务器: 内DNS:供此区域的系统VM使用,提供点的专用IP必须路由到此服务器: 如:你在添加存储设备时,用的主机 ...