先来讲解一下Semaphore信号灯的作用:
可以维护当前访问自身的线程个数,并提供了同步机制,
使用semaphore可以控制同时访问资源的线程个数
例如,实现一个文件允许的并发访问数。
请看下面的演示代码:
1 public classSemaphoreTest2 {3 public static voidmain(String[] args)4 {5 //创建一个带有缓存的线程池
6 ExecutorService service =Executors.newCachedThreadPool();7 //创建三个信号灯
8 final Semaphore sp = new Semaphore(3);//最多并发三个线程 此处可以按照需求去修改9 //开启十个线程
10 for (int i = 1; i <= 10; i++)11 {12 //只有三个线程可以同时进入 其余线程等待
13 service.execute(newRunnable()14 {15 @Override16 public voidrun()17 {18 try
19 {20 sp.acquire();//获取一盏信号灯
21 } catch(InterruptedException e)22 {23 e.printStackTrace();24 }25 System.out.println("线程 "+Thread.currentThread().getName()+" 进入"
26 + " ,当前已有 "+(3-sp.availablePermits())+ " 个并发");27 try
28 {29 Thread.sleep(new Random().nextInt(1000));30 } catch(InterruptedException e)31 {32 e.printStackTrace();33 }34 System.out.println("线程 "+Thread.currentThread().getName()+" 即将离开 ");35 sp.release();//释放
36 System.out.println("线程 "+Thread.currentThread().getName()+" 已经离开"
37 + " ,当前已有 "+(3-sp.availablePermits())+ " 个并发");38 }39 });40 }41 service.shutdown();42 }43 }
执行结果如下:
线程 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 个并发
View Code
Semaphore信号灯可以控制并发数,保证每次最多只能有三个线程在线程池中。
CyclicBarrier类的使用,可以模拟现实生活中的多人等待上车的情形,例如多人去旅行,那么当A到达集合点时,不能立即出发,必须等到B也到达集合点,那么A和B必须等到C也到达集合点,此时,三人可以坐车出发去下一站。该类就可以实现此功能,请看如下代码。
1 public classCyclicBarrierTest2 {3 public static voidmain(String[] args)4 {5 //创建一个带有缓存的线程池
6 ExecutorService service =Executors.newCachedThreadPool();7 //指定三个线程 只有当三个线程同时到达时 程序才会往下执行
8 final CyclicBarrier cb = new CyclicBarrier(3);9
10 for (int i = 0; i < 3; i++)11 {12 Runnable runnable = newRunnable()13 {14 @Override15 public voidrun()16 {17 try
18 {19 /**
20 * cb.getNumberWaiting():从0开始,获取当前等待的线程数量21 */
22 //第一个
23 Thread.sleep(new Random().nextInt(1000));24 System.out.println("线程 "+Thread.currentThread().getName()+" 即将到达集合地点1,"
25 + "当前已有"+(cb.getNumberWaiting()+1)+" 个线程,"+(cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在继续等待"));26 cb.await();//让线程等待27
28 //第二个
29 Thread.sleep(new Random().nextInt(1000));30 System.out.println("线程 "+Thread.currentThread().getName()+" 即将到达集合地点2,"
31 + "当前已有"+(cb.getNumberWaiting()+1)+" 个线程,"+(cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在继续等待"));32 cb.await();33
34 //第三个
35 Thread.sleep(new Random().nextInt(1000));36 System.out.println("线程 "+Thread.currentThread().getName()+" 即将到达集合地点3,"
37 + "当前已有"+(cb.getNumberWaiting()+1)+" 个线程,"+(cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在继续等待"));38 cb.await();39 } catch (InterruptedException |BrokenBarrierException e)40 {41 e.printStackTrace();42 }43 }44 };45 service.execute(runnable);46 }47
48 service.shutdown();//关闭线程池
49 }50 }
如下是执行结果:
1 线程 pool-1-thread-2即将到达集合地点1,当前已有1 个线程,正在继续等待2 线程 pool-1-thread-1即将到达集合地点1,当前已有2 个线程,正在继续等待3 线程 pool-1-thread-3即将到达集合地点1,当前已有3 个线程,都到齐了,继续走啊4 线程 pool-1-thread-2即将到达集合地点2,当前已有1 个线程,正在继续等待5 线程 pool-1-thread-1即将到达集合地点2,当前已有2 个线程,正在继续等待6 线程 pool-1-thread-3即将到达集合地点2,当前已有3 个线程,都到齐了,继续走啊7 线程 pool-1-thread-1即将到达集合地点3,当前已有1 个线程,正在继续等待8 线程 pool-1-thread-2即将到达集合地点3,当前已有2 个线程,正在继续等待9 线程 pool-1-thread-3 即将到达集合地点3,当前已有3 个线程,都到齐了,继续走啊
View Code
CountDownLatch计数器的使用:
* 演示一个计数器CountDownLatch
* 模拟百米赛跑
* 1个裁判 吹口哨
* 3个运动员
1 public classCountDownLatchTest2 {3 public static voidmain(String[] args)4 {5 //创建一个带有缓存的线程池
6 ExecutorService service =Executors.newCachedThreadPool();7 final CountDownLatch cdOrder = new CountDownLatch(1);//裁判
8 final CountDownLatch cdAnswer = new CountDownLatch(3);//运动员
9 for (int i = 0; i < 3; i++)10 {11 Runnable runnable = newRunnable()12 {13 @Override14 public voidrun()15 {16 try
17 {18 System.out.println("线程"+Thread.currentThread().getName()+" 正准备接收命令 ");19 cdOrder.await();//等待计数器归0时 代码向下走
20 System.out.println("线程"+Thread.currentThread().getName()+" 已接收命令 ");21 Thread.sleep(new Random().nextInt(1000));22 System.out.println("线程"+Thread.currentThread().getName()+" 回应命令处理结果 ");23 cdAnswer.countDown();//没调用一次该方法 就会将当前计数器上的计数减1
24 } catch(InterruptedException e)25 {26 e.printStackTrace();27 }28 }29 };30 service.execute(runnable);31 }32
33
34 //主线恒
35 try
36 {37 Thread.sleep(new Random().nextInt(1000));38 System.out.println("线程"+Thread.currentThread().getName()+" 即将发布命令 ");39 cdOrder.countDown();//相当于把计数器身上的计数减1
40 System.out.println("线程"+Thread.currentThread().getName()+" 已发送命令,正在等待结果 ");41 cdAnswer.await();42 System.out.println("线程"+Thread.currentThread().getName()+" 已收到所有相应结果");43 } catch(InterruptedException e)44 {45 e.printStackTrace();46 }47 service.shutdown();//关闭线程池
48 }49 }
如下是执行结果:
1 线程pool-1-thread-1正准备接收命令2 线程pool-1-thread-2正准备接收命令3 线程pool-1-thread-3正准备接收命令4 线程main 即将发布命令5 线程main 已发送命令,正在等待结果6 线程pool-1-thread-1已接收命令7 线程pool-1-thread-2已接收命令8 线程pool-1-thread-3已接收命令9 线程pool-1-thread-2回应命令处理结果10 线程pool-1-thread-3回应命令处理结果11 线程pool-1-thread-1回应命令处理结果12 线程main 已收到所有相应结果
View Code
Exchanger类可以实现两个线程之间的数据交换:
1 public classExchangerTest2 {3 public static voidmain(String[] args)4 {5 //创建一个带有缓存的线程池
6 ExecutorService service =Executors.newCachedThreadPool();7 final Exchanger changer = new Exchanger();8 //开启第一个任务
9 service.execute(newRunnable()10 {11 @Override12 public voidrun()13 {14 try
15 {16 String dtail1 = "zhangsan";//准备要交换出去的数据
17 System.out.println("线程 "+Thread.currentThread().getName()+" 正要把"+dtail1+"换出去");18 Thread.sleep(new Random().nextInt(1000));19 String dtail2 = changer.exchange(dtail1);//换回来的数据
20 System.out.println("线程 "+Thread.currentThread().getName()+"换回的数据为"+dtail2);21 } catch(InterruptedException e)22 {23 e.printStackTrace();24 }25 }26 });27
28 //开启第二个任务
29 service.execute(newRunnable()30 {31 @Override32 public voidrun()33 {34 try
35 {36 String dtail1 = "lisi";//准备要交换出去的数据
37 System.out.println("线程 "+Thread.currentThread().getName()+" 正要把"+dtail1+"换出去");38 Thread.sleep(new Random().nextInt(1000));39 String dtail2 = changer.exchange(dtail1);//换回来的数据
40 System.out.println("线程 "+Thread.currentThread().getName()+"换回的数据为"+dtail2);41 } catch(InterruptedException e)42 {43 e.printStackTrace();44 }45 }46 });47 service.shutdown();48 }49 }
如下是执行结果:
1 线程 pool-1-thread-1正要把zhangsan换出去2 线程 pool-1-thread-2正要把lisi换出去3 线程 pool-1-thread-1换回的数据为lisi4 线程 pool-1-thread-2换回的数据为zhangsan
View Code
以上都是java 5中的一些知识点,大家可以根据实际工作中的需要进行选择使用!!