CountDownLatch

    CountDownLatch是JDK1.5位于java.util.concurrent包下的一个同步辅助类,利用它利用实现类似计数器的功能。比如有一个任务A,它要等待其他所有任务执行完毕之后才能执行,这时我们就可以使用CountDownLatch来实现这种功能了。

 

    CountDownLatch只有一个带参的构造器CountDownLatch(int count),其中参数count表示计数器的值。

 

    在它的所有方法中,await()countDown()是最为重要的。其中await()可以使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。而每次调用countDown()方法时就会递减锁存器的计数,如果计数到达零,则所有等待的线程会被释放。

 

    下面我们通过例子来体会CountDownLatch的用法

    问题:假设有8名考生要参加某一学科的考试,在教室里有一个监考老师,只有监考老师准备好后才能发试卷,然后当所有考生都完成考试之后监考老师才能收试卷离开考场。

 

    对于上面问题,我们可以创建两个CountDownLatch,参数count值分别为18,表示监考老师和8名考生。然后启动8个线程(8名学生),每个线程执行前都先调用监考老师CountDownLatchawait()等待,直到监考老师发出开始考试命令后再开始考试(等待的线程被唤醒执行);同理,监考老师在所有考生都考完之前也一直等待(调用考生CountDownLatchawait()方法),每个考生考完后都调用countDown()使计数被减一,当最后一个考生考完之后计数达到零,等待着的监考老师被唤醒继续往下执行(收试卷)。

 

    基于上面的分析,代码如下: 


package com.gk.thread.latch;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CountDownLatch;

public class Exam {
	
	public static void main(String[] args) throws InterruptedException {
		
		final int NUMBER = 8;	// 有8名考生
		
		CountDownLatch teacher = new CountDownLatch(1);
		CountDownLatch student = new CountDownLatch(NUMBER);
		
		Runnable er = new ExamRunnable(teacher, student);
		for (int i=1; i<=NUMBER; i++) {
			String name = " student--" + i + " ";
			new Thread(er, name).start();
		}
		
		/*
		 * 模拟监考老师等待1分钟之后发试卷,
		 * 在这之前所有的学生线程都必须等待
		 */
		Thread.sleep(1 * 1000 * 60);
		
		System.out.println("=============" + 
							new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "开始考试" + 
							"===============");
		System.out.println();
		
		
		/*
		 * 由于teacher的计数器值为1,所以调用此方法后计数为零
		 * 所有由于调用teacher的await()方法而阻塞等待的线程都将被唤醒
		 */
		teacher.countDown();	
		
		
		student.await();	// 在student的计数为零之前阻塞监考老师线程(在这里为主线程)
		
		System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + 
							"所有考试已考试完毕,监考老师可以收试卷了...");
	}

}


package com.gk.thread.latch;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CountDownLatch;

public class ExamRunnable implements Runnable {

	private CountDownLatch teacher;
	private CountDownLatch student;
	
	public ExamRunnable(CountDownLatch teacher, CountDownLatch student) {
		this.teacher = teacher;
		this.student = student;
	}
	
	@Override
	public void run() {
		
		try {
			Thread.sleep((long)(Math.random() * 1000) * 60);	// 模拟考生陆续到达考场
		} catch (InterruptedException e) {
			throw new RuntimeException(e);
		}
		
		System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + 
							Thread.currentThread().getName() + "已经准备好,等待考试...");
		System.out.println();
		
		try {
			
			teacher.await();	// 在teacher的计数为零之前阻塞所有学生线程
			Thread.sleep((long)(Math.random() * 1000) * 60 * 5);	// 模拟考生答题时间
			
		} catch (InterruptedException e) {
			throw new RuntimeException(e);
		}
		
		System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + 
							Thread.currentThread().getName() + "已经考试完毕,离开考场...");
		System.out.println();
		
		/*
		 * 每个考生考完之后都调用countDown()使计数减一,
		 * 当最后一个考生考完之后计数为零,监考老师线程(主线程)被释放
		 */
		student.countDown();	
	}

}



    上面示例使用的是await()方法,它还有另一重载方法await(long timeout, TimeUnit unit)。两者的不同之处在于前者的返回值为void,后者的返回值为boolean(当返回值为true时表明当前的计数器为零,如果在计数到达零之前超过了等待时间则返回false);前面说过调用countDown()方法使计数到达零可使由于调用await()方法而处于阻塞状态的线程被唤醒,所以两者的另一个不同之处在于后者可以设置阻塞时间,即当超出指定的等待时间后,由于调用await方法而处于阻塞状态线程也会被唤醒。


package com.gk.thread.latch;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class  TimeDemo {
	
	public static void main(String[] args) {
		
		CountDownLatch cdl = new CountDownLatch(1);
		
		new Thread() {
			
			@Override
			public void run() {
				boolean bool;
				System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "  开始等待...");
				try {
					bool = cdl.await(2L, TimeUnit.SECONDS);
					
					//cdl.await();
				} catch (InterruptedException e) {
					throw new RuntimeException(e);
				}
				System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "  等待时间超过2秒,不和你们玩了...");
				System.out.println("boolean : " + bool);
			}
			
		}.start();
		
		new Thread() {
			
			@Override
			public void run() { 
				
				try {
					Thread.sleep(1 * 1000 * 5);	// 等待5秒之后再释放
				} catch (InterruptedException e) {
					throw new RuntimeException(e);
				}
				System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "  等待时间结束啦...");
				cdl.countDown();
			}
			
		}.start();
		
	}

}


    上面示例创建了两个线程,一个调用CountDownLatchawait方法等待2秒钟,另一个线程则等待5秒之后调用CountDownLatchcountDown()方法释放由于调用了await方法而等待的线程。不过由于await的时间只有2秒,所以没有等到5秒之后就已经“醒”了。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值