• java 5线程中 Semaphore信号灯,CyclicBarrier类,CountDownLatch计数器以及Exchanger类使用


    先来讲解一下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 个并发
    View Code

    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 个线程,都到齐了,继续走啊
    View Code

    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 已收到所有相应结果
    View Code

    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
    View Code

    以上都是java 5中的一些知识点,大家可以根据实际工作中的需要进行选择使用!!

  • 相关阅读:
    youku客户端
    youku服务端
    [计算机网络]从页面输入网址,回车到显示内容,这中间到底经历了什么
    [碎碎念]在嘛,吱一声.吱...
    [数据结构与算法]08 队列( Queue )以及队列实现的三种方式( Java 版)
    过年了,随便说点儿啥
    [碎碎念]为什么要写易维护的代码
    [碎碎念]如何成为更好的自己
    [年终总结]愿你在以后的黑暗时刻,都能想起我
    [数据结构与算法]07 关于单链表环的操作( Java 版)
  • 原文地址:https://www.cnblogs.com/little-fly/p/7600549.html
Copyright © 2020-2023  润新知