利用CountDownLatch同步辅助类进行线程同步

今天又分析了下公司网站搜索的代码。代码里用了不少的线程池(ExecutorService)。当时赶工写的代码里为了进行线程同步,每次要用线程池的时候都新建了线程池,圧入Runnable代码,shutdown()然后等线程池isTerminated() 。这样线程同步是很有效,不过每次处理搜索请求的时候都要建数个线程池、数十个线程,不能重复利用,确实是增加的系统开销。今天查阅了JAVA API文档,发现了java.util.concurrent 包下的CountDownLatch类和CyclicBarrier类可以用来进行线程同步。这下线程池可以不用shutdown而且可以一直被复用了。利用CountDownLatch类实现了一个线程同步的小Demo,效果还不错,这里拿来与诸君共勉。

代码可从敝人github下载https://github.com/DowenLiu126/syncDemo

Demo代码模拟了生活场景:早上起床后到上班后一系列日常。

敝人喜欢早上在上卫生间的时候玩会平板、刷下网页,然后煮两鸡蛋,洗下昨天换下的衣服,吃了早饭再骑自行车上班。如果每件事都使用一个线程表示,这显然是一个多线程并发过程,并且需要线程同步控制(应该极少有人会上厕所和吃饭同时进行。。。。。。)

代码中声明了一个代码日常的类Daily:

	static abstract class Daily implements Runnable {

		private final CountDownLatch startSignal;
		private final CountDownLatch doneSignal;

		public Daily(CountDownLatch startSignal, CountDownLatch doneSignal) {
			super();
			this.startSignal = startSignal;
			this.doneSignal = doneSignal;
		}

		@Override
		public void run() {
			try {
				if (this.startSignal != null)
					this.startSignal.await();
				this.doRun();
				this.doneSignal.countDown();
			} catch (InterruptedException e) {
			}
		}

		protected abstract void doRun();

	}

Daily日常活动类中声明了两个CountDownLatch同步类类型属性startSignal和doneSignal,前者表示此活动开始需要具备的条件,后者表示此活动完成时形成的影响。CountDownLatch类对象初始化时必须指定一个count值,其每调用一次countDown()方法,count值就会减小1。在一个CountDownLatch对象的count值变为0或更小之前,执行此对象的await()方法的线程会一直阻塞。Daily中实现了Runnable接口可以被ExecutorService执行,其run()方法中使用模板模式,执行业务代码前先待开始信号this.startSignal.await(); 之后消减结束信号 this.doneSignal.countDown();

每天第一件事不需要开始信号,所以有

		CountDownLatch toiletDoneSignal = new CountDownLatch(2); // 两件事要做,呃,厕所里...
		Daily toilet = new Daily(null, toiletDoneSignal) {
			@Overrideprotected void doRun() {
				System.out.println("起床上厕所...");
				try {
					Thread.sleep(3000 + r.nextInt(1000)); // 三秒神速。。。意思到了就行吧!
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("上完厕所,神清气爽!");
			}
		};
		Daily news = new Daily(null, toiletDoneSignal) {
			@Overrideprotected void doRun() {
				System.out.println("拿出平板看新闻...");
				try {
					Thread.sleep(2000 + r.nextInt(500));
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("嗯,今天没有飞机掉下来,和平和平!");
			}
		};

前一组活动具有相同的结束信号,且后继活动需要前一组活动结束,即前一组的结束信号是后继活动的开始信号

		CountDownLatch beforeBreakfastDoneSingal = new CountDownLatch(3); // 吃饭之前有3件事要做,起码先做饭吧。
		Daily cook = new Daily(toiletDoneSignal, beforeBreakfastDoneSingal) {
			@Override
			protected void doRun() {
				synchronized (lockMe) { // 得看着锅,不能去洗衣服了
					System.out.println("今天煮白水面,可怜人啊!");
					try {
						Thread.sleep(1500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println("煮好了,可怜吧吧的盛出来。");
				}
			}
		};

经过线程同步,各活动的前后顺序就被限制了。执行,输出结果如下

起床上厕所...
拿出平板看新闻...
嗯,今天没有飞机掉下来,和平和平!
上完厕所,神清气爽!
洗刷刷洗刷刷洗...
嘿嘿,我有小熊煮蛋器,每天都有煮蛋吃!
鸡蛋煮好了,凉着!
嗯,洗的很干净!
今天煮白水面,可怜人啊!
煮好了,可怜吧吧的盛出来。
能量填充中!
多谢款待!
上班赶路,嘿咻嘿咻!!!
到了公司,写了这个代码...

代码中为了能更贴近实际,对洗衣服和做饭两件事做了互斥处理,因为没办法一边看着锅一边洗衣服啊(敝人是没办法同时干这两件事的)。关于synchronized关键字的使用,请自行百度或@一下自己的大学老师(小心被K),我就不赘述了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值