现在的位置: 首页 > 综合 > 正文

Java线程之CountDownLatch

2013年12月07日 ⁄ 综合 ⁄ 共 3540字 ⁄ 字号 评论关闭

CountDownLatch是一个高级的线程同步工具,可以理解为一个闸门,先运行一些线程,计算出一些结果,满足条件后,开闸,然后另一些线程启动。换句话说,它将多个线程分为2类,一类开闸前运行,一类开闸后运行。

对于开闸后运行的线程,调用await()方法后,进入等待状态,等待开闸。它内部有一个计数器,初始状态计数器为用户设定的正整数,当开闸前某个线程运行完毕后,调用它的countDown()方法将计数器减1,当计数器为0时,开闸。

典型应用:统计硬盘上文件的数目。针对硬盘的每一个分区,例如C盘、D盘、E盘等,单独开一个线程统计该分区内的文件数。另设一个线程对所有分区的文件数汇总,显示给用户,此线程运行在开闸后,必须等每个分区都统计完毕之后,开闸,汇总报告给用户。countDown()方法最好不要由开闸前的线程直接调用,而应该由汇总线程对其进行封装,开闸前的线程之调用封装后的方法,CountDownLatch尽量对开闸前的线程透明。

/**
 * 负责将多线程查找的各分区文件数目合并汇总,
 * 打印出硬盘上存在的总文件数
 */
class CombineResult implements Runnable {

	private CountDownLatch latch;
	private long fileCount;
	
	public CombineResult(int n) {
		latch = new CountDownLatch(n);
		fileCount = 0;
	}
	
	public synchronized void commitResult(long count) {
		fileCount += count;
		latch.countDown();
	}
	
	@Override
	public void run() {
		try {
			latch.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("\n硬盘上共有" + fileCount + "个文件");
	}
}

/**
 * 统计一个硬盘分区内的文件数
 */
class CountFilesInPartition implements Runnable{
	private CombineResult result;
	private Path partition;	//需要Java 1.7支持
	private long count;

	public CountFilesInPartition(CombineResult result, Path partition) {
		this.result = result;
		this.partition = partition;
		count = 0;
	}

	@Override
	public void run() {
		long n;
		n = countFiles(partition);
		result.commitResult(n);
	}
	
	private long countFiles(Path p) {
		if(Files.isDirectory(p)) {
			try(DirectoryStream<Path> paths = Files.newDirectoryStream(p)) {
				for(Path path:paths) {
					count = countFiles(path);
				}
			} catch (IOException e) {
			}
		}else {
			count++;
		}
		return count;
	}
}

/**
 * 进度条
 */
class ProgressBar extends Thread{
	private int interval;	//显示进度的时间间隔,单位是毫秒

	public ProgressBar(int interval) {
		this.interval = interval;
		setDaemon(true);
	}

	@Override
	public void run() {
		System.out.print("正在进行中");
		while(true) {
			try {
				Thread.sleep(interval);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.print(" .");
		}
	}
}

public class CountFile {
	public static void main(String[] args) {
		int ptCount = getPartitionCount();
		CombineResult result = new CombineResult(ptCount);
		new Thread(result).start();
		Iterable<Path> partitions = FileSystems.getDefault().getRootDirectories();
		for(Path p:partitions) {
			new Thread(new CountFilesInPartition(result, p)).start();
		}
		new ProgressBar(1000).start();
	}
	
	private static int getPartitionCount() {
		Iterable<Path> partitions = FileSystems.getDefault().getRootDirectories();
		int n = 0;
		for(Path p:partitions) {
			n++;
		}
		return n;
	}
}

以下是不使用CountDownLatch,而是直接使用Lock和Condition的实现,做一个对比

public class CountFiles {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		long start = System.currentTimeMillis();
		Iterable<Path> drivers = FileSystems.getDefault().getRootDirectories();
		int n = 0;
		for(Path driver:drivers) {
			n++;
		}
		Result r = new Result(n);
		for(Path driver:drivers) {
			new MyThread(driver, r).start();
		}
		System.out.println(r.getFileCount());
		System.out.println(System.currentTimeMillis()-start);
	}
	
	private static class MyThread extends Thread {

		private Path driver;
		private long count;
		private Result result;
		
		public MyThread(Path driver, Result r) {
			super();
			this.driver = driver;
			count = 0;
			result = r;
		}

		@Override
		public void run() {
			countFiles(driver);
			result.addFileCount(count);
			result.cutThreadCount();
		}
		
		private void countFiles(Path path) {
			if(Files.isDirectory(path)) {
				DirectoryStream<Path> paths;
				try {
					paths = Files.newDirectoryStream(path);
					for(Path p:paths) {
						countFiles(p);
					}
				} catch (IOException e) {
				}
			}else {
				count++;
			}
		}
		
	}

	private static class Result {
		private long fileCount = 0;
		private int threadCount = 0;
		
		public Result(int n) {
			threadCount = n;
		}
		
		public synchronized void addFileCount(long n) {
			fileCount += n;
		}
		
		public synchronized long getFileCount() {
			while(threadCount >0) {
				try {
					wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			return fileCount;
		}
		
		public synchronized void cutThreadCount() {
			threadCount--;
			notifyAll();
		}
	}
}

抱歉!评论已关闭.