必威体育Betway必威体育官网
当前位置:首页 > IT技术

CyclicBarrier的工作原理及其实例

时间:2019-10-04 18:11:06来源:IT技术作者:seo实验室小编阅读:80次「手机版」
 

cyclicbarrier

CyclicBarrier是多线程中一个重要的类,主要用于线程组内部之间的线程的相互等待问题。

 1.CyclicBarrier的工作原理      

CyclicBarrier大致是可循环利用的屏障,顾名思义,这个名字也将这个类的特点给明确地表示出来了。首先,便是可重复利用,说明该类创建的对象可以复用;其次,屏障则体现了该类的原理:每个线程执行时,都会碰到一个屏障,直到所有线程执行结束,然后屏障便会打开,使所有线程继续往下执行。

这里介绍CyclicBarrier的两个构造函数:CyclicBarrier(int parties)和CyclicBarrier(int parties, Runnable barrierAction) :前者只需要声明需要拦截的线程数即可,而后者还需要定义一个等待所有线程到达屏障优先执行的Runnable对象。

实现原理:在CyclicBarrier的内部定义了一个Lock对象,每当一个线程调用await方法时,将拦截的线程数减1,然后判断剩余拦截数是否为初始值parties,如果不是,进入Lock对象的条件队列等待。如果是,执行barrierAction对象的Runnable方法,然后将锁的条件队列中的所有线程放入锁等待队列中,这些线程会依次的获取锁、释放锁。

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

package concurrent;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.*;
import java.util.concurrent.Linkedblockingqueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.*;
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对象的复用特性。

package concurrent;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.*;
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的工作原理以及复用的特性。

2.通过countdownlatch实现CyclicBarrier

CountDownLatch通过将await()方法和countDown()方法在不同线程组分别调用,从而实现线程组间的线程等待,即一个线程组等待另一个线程组执行结束再执行。而CyclicBarrier类则是通过调用await()方法实现线程组内的线程等待,即达到需要拦截的线程数,被拦截的线程才会依次获取锁,释放锁。那么将CountDownLatch的用法进行转换,即在同一个线程组内调用await()方法和countDown()方法,则可实现CyclicBarrier类的功能。但是注意的是必须先调用countDown()方法,才能调用await()方法,因为一旦调用await()方法,该线程后面的内容便不再执行,那么count值无法改变。具体代码如下:

package concurrent;

import java.util.vector;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class CyclicBarrierWithCount {
	private final static CountDownLatch cdl=new CountDownLatch(3);
	private final static ThreadPoolExecutor threadPool= new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());//使用线程池
	
	private static class GoThread extends Thread{
		private final String name;
		
		public GoThread(String name)
		{
			this.name=name;
			
		}
		public void run()
		{
			System.out.println(name+"开始从宿舍出发");
			cdl.countDown();
			try
			{
				Thread.sleep(1000);
				cdl.await();//拦截线程
				System.out.println(name+"从楼底下出发");
				Thread.sleep(1000);
				System.out.println(name+"到达操场");
			}
			catch(InterruptedException 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进行复用.....
又来了一拨人,看看愿不愿意一起打:
王二开始从宿舍出发
洪光开始从宿舍出发
雷兵开始从宿舍出发
赵三开始从宿舍出发
王二从楼底下出发
洪光从楼底下出发
雷兵从楼底下出发
赵三从楼底下出发
洪光到达操场
王二到达操场
雷兵到达操场
赵三到达操场
四个人一起到达球场,表示愿意一起打球,现在八个人开始打球
        由上面可知,CountDownLatch一定情况下可以实现CyclicBarrier类的功能。

3.CountDownLatch和CyclicBarrier的比较

1.CountDownLatch是线程组之间的等待,即一个(或多个)线程等待N个线程完成某件事情之后再执行;而CyclicBarrier则是线程组内的等待,即每个线程相互等待,即N个线程都被拦截之后,然后依次执行。

2.CountDownLatch是减计数方式,而CyclicBarrier是加计数方式。

3.CountDownLatch计数为0无法重置,而CyclicBarrier计数达到初始值,则可以重置。

4.CountDownLatch不可以复用,而CyclicBarrier可以复用。

相关阅读

ADODB介绍及其连接mysql简单实例

首先你想要理解数据库抽象层的概念,理解这时候你就知道ADODB的作用了,php虽然对大多数数据库都支持,但是每种数据库都有很大差异,这样

VS C#调试:未将对象引用设置到对象的实例

除了网上提到的众多原因,这里提到的是另一种情况,也会有以下提示产生。 该情况下代码无问题,原因是提示有问题的代码句或段在整段代

计算机最最最底层的 工作原理是怎么运行的

转载的这个问题从高中就开始疑惑,计算机究竟是如何理解人类思维,如何进行计算?我很想知道最最基本的工作原理,但是大学里好多课程,数字

UNreal 创建一个简单的玻璃材质+金属材质+创建材质实

首先建立一个简单的玻璃材质 创建金属材质  效果如左图,上面连接图中的texture sample  的实现有好几种方法。这里介绍最

Python爬虫教程-16-破解js加密实例(有道在线翻译)

python爬虫教程-16-破解js加密实例(有道在线翻译) 在爬虫爬取网站的时候,经常遇到一些反爬虫技术,比如: 加cookie,身份验证UserAgent

分享到:

栏目导航

推荐阅读

热门阅读