关于栅栏,闭锁,信号量

闭锁 CountDownLatch:可以自主设置一个设了多道锁的门,然后当锁全部打开的时候,门就会打开,门之后的代码得到执行。

                                主要是强调  门之后的代码必须在 在门完全打开之后才可以执行。

package test;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * 闭锁
 * @author callmehua
 *
 */
public class test01 {
	public static void main(String[] args) {
		//闭锁
		CountDownLatch cdl=new CountDownLatch(3);
		//固定长度的线程池  当然你可以直接用Thread
		ExecutorService  service=Executors.newFixedThreadPool(3);
		//开启线程
		for(int i=0;i<3;i++) {
			service.submit(new Runnable() {
				@Override
				public void run() {
						System.out.println("--我的名字是--"+Thread.currentThread().getName()+"我是 sleep 之前的打印!");
						try {
							Thread.sleep(2000);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						//下面这三句 代码 每个线程互不干扰  即我干我的 你干你的 只不过每个线程开了一次锁
						System.out.println("--我的名字是--"+Thread.currentThread().getName()+"我要开锁了!");
						cdl.countDown();
						//不管每个线程开多少锁  或者有的线程不开锁  只要让锁的计数器变为0  则调取 await()方法之后的代码就会执行
						//cdl.countDown();
						System.out.println("--我的名字是--"+Thread.currentThread().getName()+"我是 countdown 方法之后的打印!");
					}
			});
		}
		System.out.println("我正在等待");
		try {
			//其实await()方法就是设置了一个点  如果触发了 计数器为0的这个事件,才继续执行;
			//如果不加awit()方法,则闭锁 就没有起到任何作用,我不管你开没开所,我都会执行
			//进行监听计数器  一旦计数器为0 则立马执行 以后的语句
			cdl.await();
			
			/**
			 * 闭锁只影响这部分代码!!!!!!!!!! 其余代码 都是随机性的  所以就不要追究为什么其他代码会有前有后的执行了,这本就是线程的意义(希望你们明白我的意思)
			 */
			System.out.println("+++++++++++++++++++++++++++"+Thread.currentThread().getName()+"+++++++这才是被影响的地区-------开始"+System.currentTimeMillis());
			System.out.println("结束了");
			System.out.println("%%%%%%%%%%%%%%%%%%"+Thread.currentThread().getName()+"%%%%%%%%%%%%这才是被影响的地区-------结束"+System.currentTimeMillis());			
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			service.shutdown();
		}
		
	}
}

执行结果:

我正在等待
--我的名字是--pool-1-thread-2我是 sleep 之前的打印!
--我的名字是--pool-1-thread-1我是 sleep 之前的打印!
--我的名字是--pool-1-thread-3我是 sleep 之前的打印!
--我的名字是--pool-1-thread-3我要开锁了!
--我的名字是--pool-1-thread-2我要开锁了!
--我的名字是--pool-1-thread-1我要开锁了!
--我的名字是--pool-1-thread-2我是 countdown 方法之后的打印!
--我的名字是--pool-1-thread-1我是 countdown 方法之后的打印!
--我的名字是--pool-1-thread-3我是 countdown 方法之后的打印!
+++++++++++++++++++++++++++main+++++++这才是被影响的地区-------开始1523679678024
结束了

%%%%%%%%%%%%%%main%%%%%%%%%%%%%这才是影响的响的地区-------结束1523679678024

栅栏 CyclicBarrier:其实 这个和闭锁有相同之处,用栅栏也可以完成以上代码,只不过是把关键方法的位置调整一下和修改同步 工具类的构造方法就行。

                        构造方法  CyclicBarrier(int parties, Runnable barrierAction),int值是必要的 设置栅栏个数。

                                                                barrierAction不是必要的,定义的是 冲破所有栅栏后所执行的代码,属于新的线程。

                        关键方法是 await()/await(int parties)  栅栏执行到await(),会先检测栅栏是否全部被冲破,如果没有被冲破,则等待栅栏全部被冲破,然后再执行await()方法之后的代码,这部分代码是属于各自线程的。注意 parties是每次执行await方法所冲破的栅栏个数。

package test;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 栅栏
 * @author callmehua
 *
 */
public class CyclicBarrierTest {
		 public static void main(String[] args) {
			 CyclicBarrier cb=new CyclicBarrier(3,new Runnable() {
				 //定义栅栏冲破之后的所做的事
				@Override
				public void run() {
					try {
						Thread.sleep(5000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					//这里也可以被影响  因为只有冲破所有栅栏 这里才会执行 (属于新的线程了 )
					System.out.println("全部冲破--------- 必须执行完此方法,才能继续-执行每个线程 await()方法之后的 语句");
				}
			 });
			//固定长度的线程池  当然你可以直接用Thread
			ExecutorService  service=Executors.newFixedThreadPool(3);
			//开启线程
			for(int i=0;i<3;i++) {
				service.submit(new Runnable() {
					@Override
					public void run() {
							
							//下面这三句 代码 每个线程互不干扰  即我干我的 你干你的 只不过每个线程开了一次锁
							System.out.println("--我的名字是--"+Thread.currentThread().getName()+"我要冲破栅栏了!");
							try {
								System.out.println("--我的名字是--"+Thread.currentThread().getName()+"我是 sleep 之前的打印!");
								
								Thread.sleep(2000);
								//设置栅栏点  初始栅栏为3 如果全都冲破了 则证明可以向下执行 此时线程之间是互相等待的
								cb.await();
								System.out.println("+++++++++++++++++++++++++++"+Thread.currentThread().getName()+"+++++++这才是被影响的地区-------开始"+System.currentTimeMillis());
								System.out.println("成功冲破");
								System.out.println("+%%%%%%%%%%%%%%%%%%%%%"+Thread.currentThread().getName()+"%%%%%%%%%%%%%%%%这才是被影响的地区-------结束"+System.currentTimeMillis());

							} catch (InterruptedException | BrokenBarrierException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
							System.out.println("--我的名字是--"+Thread.currentThread().getName()+"我是 await() 方法之后的打印!");
						}
				});
			}
			System.out.println("不被影响");
			try {
				
				
				
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}finally {
				service.shutdown();
			}
				
		}
}

运行结果:

不被影响
--我的名字是--pool-1-thread-1我要冲破栅栏了!
--我的名字是--pool-1-thread-1我是 sleep 之前的打印!
--我的名字是--pool-1-thread-3我要冲破栅栏了!
--我的名字是--pool-1-thread-2我要冲破栅栏了!
--我的名字是--pool-1-thread-2我是 sleep 之前的打印!
--我的名字是--pool-1-thread-3我是 sleep 之前的打印!
全部冲破--------- 必须执行完此方法,才能继续-执行每个线程 await()方法之后的 语句
+++++++++++++++++++++++++++pool-1-thread-2+++++++这才是被影响的地区-------开始1523681082181
成功冲破
+++++++++++++++++++++++++++pool-1-thread-1+++++++这才是被影响的地区-------开始1523681082181
成功冲破
+++++++++++++++++++++++++++pool-1-thread-3+++++++这才是被影响的地区-------开始1523681082181
成功冲破
+%%%%%%%%%%%%%%%%%%%%%pool-1-thread-1%%%%%%%%%%%%%%%%这才是被影响的地区-------结束1523681082181
+%%%%%%%%%%%%%%%%%%%%%pool-1-thread-2%%%%%%%%%%%%%%%%这才是被影响的地区-------结束1523681082181
--我的名字是--pool-1-thread-2我是 await() 方法之后的打印!
--我的名字是--pool-1-thread-1我是 await() 方法之后的打印!
+%%%%%%%%%%%%%%%%%%%%%pool-1-thread-3%%%%%%%%%%%%%%%%这才是被影响的地区-------结束1523681082181

--我的名字是--pool-1-thread-3我是 await() 方法之后的打印!

注意:若要用栅栏实现闭锁的那个例子,只需要把 闭锁影响区域的代码放到 栅栏构造方法中第二参数中即可,并且调整栅栏中await()之后的代码。归根结底,栅栏更加灵活,因为可以对每个线程设置栅栏冲破后各自的实现,而闭锁做不到(我是这样理解的)。

信号量  Semaphore:和synchronized差不多,只不过synchronized更加严格一点,只能一个线程可以拿到锁!信号量比较温柔,你可以进来试着看看能不能拿到‘锁’,如果拿到就继续执行,不能拿到就等着。信号量的‘锁’其实就是资源。

package test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class SemaphoreTest {
	public static void main(String[] args) {
		
		Semaphore sema=new Semaphore(3);
		//定义线程数量
		 int count = 5;
		//固定长度的线程池  当然你可以直接用Thread
		ExecutorService  service=Executors.newFixedThreadPool(count);
		
		//开启线程
		for(int i=0;i<count;i++) {
			service.submit(new Runnable() {
				@Override
				public void run() {
						try {
							System.out.println("--我的名字是--"+Thread.currentThread().getName()+"我要**试试**拿到 '锁' !"+System.currentTimeMillis());
							//获取信息量
							sema.acquire();
						System.out.println("+++++++++++++++++++++++++++"+Thread.currentThread().getName()+"+++++++这才是被锁的地区-------开始"+System.currentTimeMillis());
							System.out.println("--我的名字是--"+Thread.currentThread().getName()+"我要>>>>>已经<<<<<拿到 '锁' !"+System.currentTimeMillis());
							//注意 信息量锁住的是 acquire()方法之后的代码   acquire()是拿到信息量,如果拿到了,则向后执行,如果没拿到,则等待
							Thread.sleep(2000);
							//把信息量还回去
							System.out.println("--我的名字是--"+Thread.currentThread().getName()+"我要======还回去====== '锁' !"+System.currentTimeMillis());
							sema.release();
							/**
							 * (在上面)切记 不要忘了重置信号量
							 */
						System.out.println("+%%%%%%%%%%%%%%%%%%%%%"+Thread.currentThread().getName()+"%%%%%%%%%%%%%%%%这才是被锁的地区-------结束"+System.currentTimeMillis());
							
						} catch (Exception e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}

						System.out.println("--我的名字是--"+Thread.currentThread().getName()+"我是拿 '锁' 方法之后的打印!"+System.currentTimeMillis());
					}
			});
		}
		try {
			System.out.println("无关紧要");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			
			service.shutdown();
		}
			
	}

}

输出结果:

无关紧要
--我的名字是--pool-1-thread-4我要**试试**拿到 '锁' !1523681692681
--我的名字是--pool-1-thread-5我要**试试**拿到 '锁' !1523681692681
+++++++++++++++++++++++++++pool-1-thread-4+++++++这才是被锁的地区-------开始1523681692681
--我的名字是--pool-1-thread-3我要**试试**拿到 '锁' !1523681692681
--我的名字是--pool-1-thread-1我要**试试**拿到 '锁' !1523681692681
--我的名字是--pool-1-thread-2我要**试试**拿到 '锁' !1523681692681
+++++++++++++++++++++++++++pool-1-thread-3+++++++这才是被锁的地区-------开始1523681692681
--我的名字是--pool-1-thread-4我要>>>>>已经<<<<<拿到 '锁' !1523681692681
+++++++++++++++++++++++++++pool-1-thread-5+++++++这才是被锁的地区-------开始1523681692681
--我的名字是--pool-1-thread-5我要>>>>>已经<<<<<拿到 '锁' !1523681692681
--我的名字是--pool-1-thread-3我要>>>>>已经<<<<<拿到 '锁' !1523681692681

--我的名字是--pool-1-thread-3我要======还回去====== '锁' !1523681694682
--我的名字是--pool-1-thread-5我要======还回去====== '锁' !1523681694682
--我的名字是--pool-1-thread-4我要======还回去====== '锁' !1523681694682

+++++++++++++++++++++++++++pool-1-thread-2+++++++这才是被锁的地区-------开始1523681694682
--我的名字是--pool-1-thread-2我要>>>>>已经<<<<<拿到 '锁' !1523681694682
%%%%%%%%%%%%%%%pool-1-thread-5%%%%%%%%%%这才是被锁的地区-------结束1523681694682
--我的名字是--pool-1-thread-5我是拿 '锁' 方法之后的打印!1523681694682
+++++++++++++++++++++++++++pool-1-thread-1+++++++这才是被锁的地区-------开始1523681694682
--我的名字是--pool-1-thread-1我要>>>>>已经<<<<<拿到 '锁' !1523681694682
%%%%%%%%%%%%%%%pool-1-thread-4%%%%%%%%%%这才是被锁的地区-------结束1523681694682
--我的名字是--pool-1-thread-4我是拿 '锁' 方法之后的打印!1523681694682
%%%%%%%%%%%%%%%pool-1-thread-3%%%%%%%%%%这才是被锁的地区-------结束1523681694682
--我的名字是--pool-1-thread-3我是拿 '锁' 方法之后的打印!1523681694682
--我的名字是--pool-1-thread-2我要======还回去====== '锁' !1523681696682
%%%%%%%%%%%%%%%pool-1-thread-2%%%%%%%%%%这才是被锁的地区-------结束1523681696682
--我的名字是--pool-1-thread-2我是拿 '锁' 方法之后的打印!1523681696682
--我的名字是--pool-1-thread-1我要======还回去====== '锁' !1523681696682
%%%%%%%%%%%%%%%pool-1-thread-1%%%%%%%%%%这才是被锁的地区-------结束1523681696682

--我的名字是--pool-1-thread-1我是拿 '锁' 方法之后的打印!1523681696682

总结:注意看代码中的 影响范围,红底色代码。

      闭锁,专注于多个线程执行到某个点,然后触发事件,再执行触发事件之后的代码。

       栅栏:比闭锁更加灵活。增强版吧。专注于多个线程互相等待执行await()之后的代码,也可以定义一个消息机制,在所有栅栏冲破后,可以执行另一个线程。

信息量:和synchronized相差无几,拿资源,拿‘锁’。只不过可以拿多个而已(一个线程拿一个,多个线程;一个线程拿多个,多个线程)。

我只是个it小白,在工作之余学习学习,在百度相关知识的时候,几乎所有的帖子都是千篇一律。。。可能大家都比我聪明吧,反正我是有点迷惑,只好动手实践下,这样时间花费的时间多是是多了,可是更加明白了。

如有错误,请大家指正。我会认真的修改。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值