zoukankan      html  css  js  c++  java
  • java CountDownLatch、CyclicBarrier和 Semaphore用法

    一.CountDownLatch用法

      CountDownLatch类位于java.util.concurrent中包下,利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。

      CountDownLatch类只提供了一个构造器:

    public CountDownLatch(int count) { };  //参数count为计数值
    

       然后下面这3个方法是CountDownLatch类中最重要的方法:

    public void await() throws InterruptedException { };   //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
    public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };  //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
    public void countDown() { };  //将count值减1

       下面看一个例子大家就清楚CountDownLatch的用法了:

    import java.util.concurrent.CountDownLatch;
    
    public class Test {
    	public static void main(String[] args) {
    		final CountDownLatch latch = new CountDownLatch(2);
    
    		new Thread() {
    			public void run() {
    				try {
    					System.out.println("子线程" + Thread.currentThread().getName() + "正在执行");
    					Thread.sleep(3000);
    					System.out.println("子线程" + Thread.currentThread().getName() + "执行完毕");
    					latch.countDown();
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			};
    		}.start();
    
    		new Thread() {
    			public void run() {
    				try {
    					System.out.println("子线程" + Thread.currentThread().getName() + "正在执行");
    					Thread.sleep(3000);
    					System.out.println("子线程" + Thread.currentThread().getName() + "执行完毕");
    					latch.countDown();
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			};
    		}.start();
    
    		try {
    			System.out.println("等待2个子线程执行完毕...");
    			latch.await();
    			System.out.println("2个子线程已经执行完毕");
    			System.out.println("继续执行主线程");
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    }

      执行结果:

    线程Thread-0正在执行
    线程Thread-1正在执行
    等待2个子线程执行完毕...
    线程Thread-0执行完毕
    线程Thread-1执行完毕
    2个子线程已经执行完毕
    继续执行主线程

    应用场景:在玩欢乐斗地主时必须等待三个玩家都到齐才可以进行发牌。

    import java.util.concurrent.CountDownLatch;
    
    public class Player extends Thread {
    
    	private static int count = 1;
    	private final int id = count++;
    	private CountDownLatch latch;
    
    	public Player(CountDownLatch latch) {
    		this.latch = latch;
    	}
    
    	@Override
    	public void run() {
    		System.out.println("【玩家" + id + "】已入场");
    		latch.countDown();
    	}
    
    	public static void main(String[] args) throws InterruptedException {
    		CountDownLatch latch = new CountDownLatch(3);
    		System.out.println("牌局开始, 等待玩家入场...");
    		new Player(latch).start();
    		new Player(latch).start();
    		new Player(latch).start();
    		latch.await();
    		System.out.println("玩家已到齐, 开始发牌...");
    	}
    
    }
    
    牌局开始, 等待玩家入场...
    【玩家1】已入场
    【玩家2】已入场
    【玩家3】已入场
    玩家已到齐, 开始发牌...

    运行结果显示发牌操作一定是在所有玩家都入场后才进行。我们将latch.await()注释掉,对比下看看结果。

    牌局开始, 等待玩家入场...
    【玩家1】已入场
    【玩家3】已入场
    玩家已到齐, 开始发牌...
    【玩家2】已入场

    可以看到在注释掉latch.await()这行之后,就不能保证在所有玩家入场后才开始发牌了。

    二.CyclicBarrier用法

      字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,的CyclicBarrier可以被重用。我们暂且把这个状态就叫做阻挡,当调用AWAIT()方法之后,线程就处于阻挡了。

      的CyclicBarrier类位于java.util.concurrent中包下,的CyclicBarrier提供2个构造器:

    public CyclicBarrier(int parties, Runnable barrierAction) { }
     
    public CyclicBarrier(int parties) { }

      参数各方指让多少个线程或者任务等待至阻挡状态;参数barrierAction为当这些线程都达到阻挡状态时会执行的内容。

      然后的CyclicBarrier中最重要的方法就是AWAIT方法,它有2个重载版本:

    public int await() throws InterruptedException, BrokenBarrierException { };
    public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };

      第二个版本是让这些线程等待至一定的时间,如果还有线程没有到达barrier状态就直接让到达barrier的线程执行后续任务。         第一个版本比较常用,用来挂起当前线程,直至所有线程都到达屏障状态再同时执行后续任务;

      下面举几个例子就明白了:

      假若有若干个线程都要进行写数据操作,并且只有所有线程都完成写数据操作之后,这些线程才能继续做后面的事情,此时就可以利用的CyclicBarrier了:

    public class Test {
        public static void main(String[] args) {
            int N = 4;
            CyclicBarrier barrier  = new CyclicBarrier(N);
            for(int i=0;i<N;i++)
                new Writer(barrier).start();
        }
        static class Writer extends Thread{
            private CyclicBarrier cyclicBarrier;
            public Writer(CyclicBarrier cyclicBarrier) {
                this.cyclicBarrier = cyclicBarrier;
            }
     
            @Override
            public void run() {
                System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
                try {
                    Thread.sleep(5000);      //以睡眠来模拟写入数据操作
                    System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }catch(BrokenBarrierException e){
                    e.printStackTrace();
                }
                System.out.println("所有线程写入完毕,继续处理其他任务...");
            }
        }
    }

       执行结果:

    线程Thread-0正在写入数据...
    线程Thread-3正在写入数据...
    线程Thread-2正在写入数据...
    线程Thread-1正在写入数据...
    线程Thread-2写入数据完毕,等待其他线程写入完毕
    线程Thread-0写入数据完毕,等待其他线程写入完毕
    线程Thread-3写入数据完毕,等待其他线程写入完毕
    线程Thread-1写入数据完毕,等待其他线程写入完毕
    所有线程写入完毕,继续处理其他任务...
    所有线程写入完毕,继续处理其他任务...
    所有线程写入完毕,继续处理其他任务...
    所有线程写入完毕,继续处理其他任务...

      从上面输出结果可以看出,每个写入线程执行完写数据操作之后,就在等待其他线程写入操作完毕。

      当所有线程线程写入操作完毕之后,所有线程就继续进行后续的操作了。

      如果说想在所有线程写入操作完之后,进行额外的其他操作可以为CyclicBarrier提供Runnable参数:

    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    
    public class Test {
    	public static void main(String[] args) {
    		int N = 4;
    		CyclicBarrier barrier = new CyclicBarrier(N, new Runnable() {
    			@Override
    			public void run() {
    				System.out.println("当前线程" + Thread.currentThread().getName());
    			}
    		});
    
    		for (int i = 0; i < N; i++)
    			new Writer(barrier).start();
    	}
    
    	static class Writer extends Thread {
    		private CyclicBarrier cyclicBarrier;
    
    		public Writer(CyclicBarrier cyclicBarrier) {
    			this.cyclicBarrier = cyclicBarrier;
    		}
    
    		@Override
    		public void run() {
    			System.out.println("线程" + Thread.currentThread().getName() + "正在写入数据...");
    			try {
    				Thread.sleep(5000); // 以睡眠来模拟写入数据操作
    				System.out.println("线程" + Thread.currentThread().getName() + "写入数据完毕,等待其他线程写入完毕");
    				cyclicBarrier.await();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			} catch (BrokenBarrierException e) {
    				e.printStackTrace();
    			}
    			System.out.println("所有线程写入完毕,继续处理其他任务...");
    		}
    	}
    }

        运行结果:

    线程Thread-0正在写入数据...
    线程Thread-1正在写入数据...
    线程Thread-2正在写入数据...
    线程Thread-3正在写入数据...
    线程Thread-0写入数据完毕,等待其他线程写入完毕
    线程Thread-1写入数据完毕,等待其他线程写入完毕
    线程Thread-2写入数据完毕,等待其他线程写入完毕
    线程Thread-3写入数据完毕,等待其他线程写入完毕
    当前线程Thread-3
    所有线程写入完毕,继续处理其他任务...
    所有线程写入完毕,继续处理其他任务...
    所有线程写入完毕,继续处理其他任务...
    所有线程写入完毕,继续处理其他任务...

      从结果可以看出,当四个线程都到达barrier状态后,会从四个线程中选择一个线程去执行Runnable。  

      举例说明:如果一个寝室四个人约好了去球场打球,由于四个人准备工作不同,所以约好在楼下集合,并且四个人集合好之后一起出发去球场。

    import java.util.concurrent.CyclicBarrier;
    import java.util.concurrent.*;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class CyclicBarrierDemo {
    	private static final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(4, 10, 60, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
    	// 当拦截线程数达到4时,便优先执行barrierAction,然后再执行被拦截的线程。
    	private static final CyclicBarrier cb = new CyclicBarrier(4, new Runnable() {
    		public void run() {
    			System.out.println("寝室四兄弟一起出发去球场");
    		}
    	});
    
    	private static class GoThread extends Thread {
    		private final String name;
    
    		public GoThread(String name) {
    			this.name = name;
    		}
    
    		public void run() {
    			System.out.println(name + "开始从宿舍出发");
    			try {
    				Thread.sleep(1000);
    				cb.await();// 拦截线程
    				System.out.println(name + "从楼底下出发");
    				Thread.sleep(1000);
    				System.out.println(name + "到达操场");
    
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			} catch (BrokenBarrierException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		String[] str = { "李明", "王强", "刘凯", "赵杰" };
    		for (int i = 0; i < 4; i++) {
    			threadPool.execute(new GoThread(str[i]));
    		}
    		try {
    			Thread.sleep(4000);
    			System.out.println("四个人一起到达球场,现在开始打球");
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    }
    

      运行程序,得到如下结果:

    李明开始从宿舍出发
    赵杰开始从宿舍出发
    王强开始从宿舍出发
    刘凯开始从宿舍出发
    寝室四兄弟一起出发去球场
    赵杰从楼底下出发
    李明从楼底下出发
    刘凯从楼底下出发
    王强从楼底下出发
    赵杰到达操场
    王强到达操场
    李明到达操场
    刘凯到达操场
    四个人一起到达球场,现在开始打球
    

      以上便是CyclicBarrier使用实例,通过await()方法对线程的拦截,拦截数加1,当拦截数为初始的parties,首先执行了barrierAction,然后对拦截的线程队列依次进行获取锁释放锁。接下来,在这个例子上讲解CyclicBarrier对象的复用特性。

    import java.util.concurrent.CyclicBarrier;
    import java.util.concurrent.*;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class CyclicBarrierDemo {
    	private static final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(4, 10, 60, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
    	// 当拦截线程数达到4时,便优先执行barrierAction,然后再执行被拦截的线程。
    	private static final CyclicBarrier cb = new CyclicBarrier(4, new Runnable() {
    		public void run() {
    			System.out.println("寝室四兄弟一起出发去球场");
    		}
    	});
    
    	private static class GoThread extends Thread {
    		private final String name;
    
    		public GoThread(String name) {
    			this.name = name;
    		}
    
    		public void run() {
    			System.out.println(name + "开始从宿舍出发");
    			try {
    				Thread.sleep(1000);
    				cb.await();// 拦截线程
    				System.out.println(name + "从楼底下出发");
    				Thread.sleep(1000);
    				System.out.println(name + "到达操场");
    
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			} catch (BrokenBarrierException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		String[] str = { "李明", "王强", "刘凯", "赵杰" };
    		String[] str1 = { "王二", "洪光", "雷兵", "赵三" };
    		for (int i = 0; i < 4; i++) {
    			threadPool.execute(new GoThread(str[i]));
    		}
    		try {
    			Thread.sleep(4000);
    			System.out.println("四个人一起到达球场,现在开始打球");
    			System.out.println("现在对CyclicBarrier进行复用.....");
    			System.out.println("又来了一拨人,看看愿不愿意一起打:");
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		// 进行复用:
    		for (int i = 0; i < 4; i++) {
    			threadPool.execute(new GoThread(str1[i]));
    		}
    		try {
    			Thread.sleep(4000);
    			System.out.println("四个人一起到达球场,表示愿意一起打球,现在八个人开始打球");
    			// System.out.println("现在对CyclicBarrier进行复用");
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    
    	}
    }

      运行如下程序,得到:

    王强开始从宿舍出发
    赵杰开始从宿舍出发
    李明开始从宿舍出发
    刘凯开始从宿舍出发
    寝室四兄弟一起出发去球场
    王强从楼底下出发
    李明从楼底下出发
    刘凯从楼底下出发
    赵杰从楼底下出发
    王强到达操场
    李明到达操场
    赵杰到达操场
    刘凯到达操场
    四个人一起到达球场,现在开始打球
    现在对CyclicBarrier进行复用.....
    又来了一拨人,看看愿不愿意一起打:
    王二开始从宿舍出发
    雷兵开始从宿舍出发
    洪光开始从宿舍出发
    赵三开始从宿舍出发
    寝室四兄弟一起出发去球场
    洪光从楼底下出发
    王二从楼底下出发
    雷兵从楼底下出发
    赵三从楼底下出发
    雷兵到达操场
    赵三到达操场
    洪光到达操场
    王二到达操场
    四个人一起到达球场,表示愿意一起打球,现在八个人开始打球
    

            由上面实例可了解CyclicBarrier的工作原理以及复用的特性。

      下面看一下为await指定时间的效果:

    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.TimeoutException;
    
    public class Test {
    	public static void main(String[] args) {
    		int N = 4;
    		CyclicBarrier barrier = new CyclicBarrier(N);
    
    		for (int i = 0; i < N; i++) {
    			if (i < N - 1)
    				new Writer(barrier).start();
    			else {
    				try {
    					Thread.sleep(5000);
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    				new Writer(barrier).start();
    			}
    		}
    	}
    
    	static class Writer extends Thread {
    		private CyclicBarrier cyclicBarrier;
    
    		public Writer(CyclicBarrier cyclicBarrier) {
    			this.cyclicBarrier = cyclicBarrier;
    		}
    
    		@Override
    		public void run() {
    			System.out.println("线程" + Thread.currentThread().getName() + "正在写入数据...");
    			try {
    				Thread.sleep(5000); // 以睡眠来模拟写入数据操作
    				System.out.println("线程" + Thread.currentThread().getName() + "写入数据完毕,等待其他线程写入完毕");
    				try {
    					cyclicBarrier.await(2000, TimeUnit.MILLISECONDS);
    				} catch (TimeoutException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			} catch (BrokenBarrierException e) {
    				e.printStackTrace();
    			}
    			System.out.println(Thread.currentThread().getName() + "所有线程写入完毕,继续处理其他任务...");
    		}
    	}
    }

       执行结果:

    线程Thread-1正在写入数据...
    线程Thread-2正在写入数据...
    线程Thread-0正在写入数据...
    线程Thread-1写入数据完毕,等待其他线程写入完毕
    线程Thread-2写入数据完毕,等待其他线程写入完毕
    线程Thread-0写入数据完毕,等待其他线程写入完毕
    线程Thread-3正在写入数据...
    java.util.concurrent.TimeoutException
    Thread-1所有线程写入完毕,继续处理其他任务...
    Thread-0所有线程写入完毕,继续处理其他任务...
    Thread-2所有线程写入完毕,继续处理其他任务...
    	at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
    	at java.util.concurrent.CyclicBarrier.await(Unknown Source)
    	at com.test.Test$Writer.run(Test.java:42)
    java.util.concurrent.BrokenBarrierException
    	at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
    	at java.util.concurrent.CyclicBarrier.await(Unknown Source)
    	at com.test.Test$Writer.run(Test.java:42)
    java.util.concurrent.BrokenBarrierException
    	at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
    	at java.util.concurrent.CyclicBarrier.await(Unknown Source)
    	at com.test.Test$Writer.run(Test.java:42)
    线程Thread-3写入数据完毕,等待其他线程写入完毕
    java.util.concurrent.BrokenBarrierException
    	at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
    	at java.util.concurrent.CyclicBarrier.await(Unknown Source)
    	at com.test.Test$Writer.run(Test.java:42)
    Thread-3所有线程写入完毕,继续处理其他任务...
    

      上面的代码在main方法的for循环中,故意让最后一个线程启动延迟,因为在前面三个线程都达到barrier之后,等待了指定的时间发现第四个线程还没有达到barrier,就抛出异常并继续执行后面的任务。

      另外CyclicBarrier是可以重用的,看下面这个例子:

    public class Test {
        public static void main(String[] args) {
            int N = 4;
            CyclicBarrier barrier  = new CyclicBarrier(N);
             
            for(int i=0;i<N;i++) {
                new Writer(barrier).start();
            }
             
            try {
                Thread.sleep(25000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
             
            System.out.println("CyclicBarrier重用");
             
            for(int i=0;i<N;i++) {
                new Writer(barrier).start();
            }
        }
        static class Writer extends Thread{
            private CyclicBarrier cyclicBarrier;
            public Writer(CyclicBarrier cyclicBarrier) {
                this.cyclicBarrier = cyclicBarrier;
            }
     
            @Override
            public void run() {
                System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
                try {
                    Thread.sleep(5000);      //以睡眠来模拟写入数据操作
                    System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
                 
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }catch(BrokenBarrierException e){
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"所有线程写入完毕,继续处理其他任务...");
            }
        }
    }

       执行结果:

    线程Thread-0正在写入数据...
    线程Thread-1正在写入数据...
    线程Thread-3正在写入数据...
    线程Thread-2正在写入数据...
    线程Thread-1写入数据完毕,等待其他线程写入完毕
    线程Thread-3写入数据完毕,等待其他线程写入完毕
    线程Thread-2写入数据完毕,等待其他线程写入完毕
    线程Thread-0写入数据完毕,等待其他线程写入完毕
    Thread-0所有线程写入完毕,继续处理其他任务...
    Thread-3所有线程写入完毕,继续处理其他任务...
    Thread-1所有线程写入完毕,继续处理其他任务...
    Thread-2所有线程写入完毕,继续处理其他任务...
    CyclicBarrier重用
    线程Thread-4正在写入数据...
    线程Thread-5正在写入数据...
    线程Thread-6正在写入数据...
    线程Thread-7正在写入数据...
    线程Thread-7写入数据完毕,等待其他线程写入完毕
    线程Thread-5写入数据完毕,等待其他线程写入完毕
    线程Thread-6写入数据完毕,等待其他线程写入完毕
    线程Thread-4写入数据完毕,等待其他线程写入完毕
    Thread-4所有线程写入完毕,继续处理其他任务...
    Thread-5所有线程写入完毕,继续处理其他任务...
    Thread-6所有线程写入完毕,继续处理其他任务...
    Thread-7所有线程写入完毕,继续处理其他任务...

      从执行结果可以看出,在初次的4个线程越过barrier状态后,又可以用来进行新一轮的使用。而CountDownLatch无法进行重复使用。

    三.Semaphore用法

      Semaphore翻译成字面意思为 信号量,Semaphore可以控同时访问的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。

      Semaphore类位于java.util.concurrent包下,它提供了2个构造器:

    public Semaphore(int permits) {          //参数permits表示许可数目,即同时可以允许多少线程进行访问
        sync = new NonfairSync(permits);
    }
    public Semaphore(int permits, boolean fair) {    //这个多了一个参数fair表示是否是公平的,即等待时间越久的越先获取许可
        sync = (fair)? new FairSync(permits) : new NonfairSync(permits);
    }

        下面说一下Semaphore类中比较重要的几个方法,首先是acquire()、release()方法:

    public void acquire() throws InterruptedException {  }     //获取一个许可
    public void acquire(int permits) throws InterruptedException { }    //获取permits个许可
    public void release() { }          //释放一个许可
    public void release(int permits) { }    //释放permits个许可

      acquire()用来获取一个许可,若无许可能够获得,则会一直等待,直到获得许可。

      release()用来释放许可。注意,在释放许可之前,必须先获获得许可。

      这4个方法都会被阻塞,如果想立即得到执行结果,可以使用下面几个方法:

    public boolean tryAcquire() { };    //尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
    public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { };  //尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
    public boolean tryAcquire(int permits) { }; //尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
    public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException { }; //尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false

       另外还可以通过availablePermits()方法得到可用的许可数目。

      下面通过一个例子来看一下Semaphore的具体使用:

      假若一个工厂有5台机器,但是有8个工人,一台机器同时只能被一个工人使用,只有使用完了,其他工人才能继续使用。那么我们就可以通过Semaphore来实现:

    import java.util.concurrent.Semaphore;
    
    public class Test {
    	public static void main(String[] args) {
    		int N = 8; // 工人数
    		Semaphore semaphore = new Semaphore(5); // 机器数目
    		for (int i = 0; i < N; i++)
    			new Worker(i, semaphore).start();
    	}
    
    	static class Worker extends Thread {
    		private int num;
    		private Semaphore semaphore;
    
    		public Worker(int num, Semaphore semaphore) {
    			this.num = num;
    			this.semaphore = semaphore;
    		}
    
    		@Override
    		public void run() {
    			try {
    				semaphore.acquire();
    				System.out.println("工人" + this.num + "占用一个机器在生产...");
    				Thread.sleep(2000);
    				System.out.println("工人" + this.num + "释放出机器");
    				semaphore.release();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }

        执行结果:

    工人0占用一个机器在生产...
    工人1占用一个机器在生产...
    工人2占用一个机器在生产...
    工人4占用一个机器在生产...
    工人5占用一个机器在生产...
    工人0释放出机器
    工人2释放出机器
    工人3占用一个机器在生产...
    工人7占用一个机器在生产...
    工人4释放出机器
    工人5释放出机器
    工人1释放出机器
    工人6占用一个机器在生产...
    工人3释放出机器
    工人7释放出机器
    工人6释放出机器

      下面对上面说的三个辅助类进行一个总结:

      1)CountDownLatch和的CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:

        CountDownLatch一般用于某个线程甲等待若干个其他线程执行完任务之后,它才执行;

        而的CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;

        另外,CountDownLatch是不能够重用的,而的CyclicBarrier是可以重用的。

      2)信号量其实和锁有点类似,它一般用于控制对某组资源的访问权限。

  • 相关阅读:
    Eclipse安装Hadoop插件
    (转)Ubuntu14.0.4中hadoop2.4.0伪分布模式配置
    Hadoop--DataNode无法启动
    启动与关闭hadoop
    hadoop中执行命令时发生错误
    strings命令
    Deriving data from ElasticSearch Engine
    elasticsearch data importing
    reading words in your computer and changing to female voice, linux festival text2wave saving wav files
    DDNS client on a Linux machine
  • 原文地址:https://www.cnblogs.com/gmhappy/p/11864088.html
Copyright © 2011-2022 走看看