先来讲解一下Semaphore信号灯的作用:
可以维护当前访问自身的线程个数,并提供了同步机制,
使用semaphore可以控制同时访问资源的线程个数
例如,实现一个文件允许的并发访问数。
请看下面的演示代码:
1 public class SemaphoreTest 2 { 3 public static void main(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(new Runnable() 14 { 15 @Override 16 public void run() 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 个并发
Semaphore信号灯可以控制并发数,保证每次最多只能有三个线程在线程池中。
CyclicBarrier类的使用,可以模拟现实生活中的多人等待上车的情形,例如多人去旅行,那么当A到达集合点时,不能立即出发,必须等到B也到达集合点,那么A和B必须等到C也到达集合点,此时,三人可以坐车出发去下一站。该类就可以实现此功能,请看如下代码。
1 public class CyclicBarrierTest 2 { 3 public static void main(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 = new Runnable() 13 { 14 @Override 15 public void run() 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 个线程,都到齐了,继续走啊
CountDownLatch计数器的使用:
* 演示一个计数器CountDownLatch
* 模拟百米赛跑
* 1个裁判 吹口哨
* 3个运动员
1 public class CountDownLatchTest 2 { 3 public static void main(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 = new Runnable() 12 { 13 @Override 14 public void run() 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 已收到所有相应结果
Exchanger类可以实现两个线程之间的数据交换:
1 public class ExchangerTest 2 { 3 public static void main(String[] args) 4 { 5 //创建一个带有缓存的线程池 6 ExecutorService service = Executors.newCachedThreadPool(); 7 final Exchanger<String> changer = new Exchanger<String>(); 8 //开启第一个任务 9 service.execute(new Runnable() 10 { 11 @Override 12 public void run() 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(new Runnable() 30 { 31 @Override 32 public void run() 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换回的数据为lisi 4 线程 pool-1-thread-2换回的数据为zhangsan
以上都是java 5中的一些知识点,大家可以根据实际工作中的需要进行选择使用!!