CountDownLatch的工作原理以及实例

        CountDownLatch、CyclicBarrier是多线程重要的类,本文主要进行对其主要原理的讲解,并且通过举例的形式,使得原理更加清晰,更易了解。

1.CountDownLatch工作原理

        CountDownLatch在多线程并发编程中充当一个计时器的功能,并且维护一个count的变量,并且其操作都是原子操作,该类主要通过countDown()和await()两个方法实现功能的,首先通过建立CountDownLatch对象,并且传入参数即为count初始值。如果一个线程调用了await()方法,那么这个线程便进入阻塞状态,并进入阻塞队列。如果一个线程调用了countDown()方法,则会使count-1;当count的值为0时,这时候阻塞队列中调用await()方法的线程便会逐个被唤醒,从而进入后续的操作。比如下面的例子就是有两个操作,一个是读操作一个是写操作,现在规定必须进行完写操作才能进行读操作。所以当最开始调用读操作时,需要用await()方法使其阻塞,当写操作结束时,则需要使count等于0。因此count的初始值可以定为写操作的记录数,这样便可以使得进行完写操作,然后进行读操作。

具体代码如下:

package concurrent;
import java.util.concurrent.CountDownLatch;
import java.util.*;
public class CountDownLatchDemo {
	
	private final static CountDownLatch cdl=new CountDownLatch(3);
	private final static Vector v=new Vector();
	
	private static class WriteThread extends Thread{
		private final String writeThreadName;
		private final int stopTime;
		private final String str;
		public WriteThread(String name,int time,String str)
		{
			this.writeThreadName=name;
			this.stopTime=time;
			this.str=str;
		}
		public void run()
		{
			System.out.println(writeThreadName+"开始写入工作");
			try
			{
				Thread.sleep(stopTime);
			}
			catch(InterruptedException e)
			{
				e.printStackTrace();
			}
			cdl.countDown();
			v.add(str);
			System.out.println(writeThreadName+"写入内容为:"+str+"。写入工作结束!");
		}
	}
	private static class ReadThread extends Thread{
		public void run()
		{
			System.out.println("读操作之前必须先进行写操作");
			try
			{
				cdl.await();//该线程进行等待,直到countDown减到0,然后逐个苏醒过来。
				//Thread.sleep(3000);
			}
			catch(InterruptedException e)
			{
				e.printStackTrace();
			}
			for(int i=0;i<v.size();i++)
			{
				System.out.println("读取第"+(i+1)+"条记录内容为:"+v.get(i));
			}
			System.out.println("读操作结束!");
		}
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		new ReadThread().start();
		new WriteThread("writeThread1",1000,"多线程知识点").start();
		new WriteThread("writeThread2",2000,"多线程CountDownLatch的知识点").start();
		new WriteThread("writeThread3",3000,"多线程中控制顺序可以使用CountDownLatch").start();

	}

}

运行代码,结果如下:

读操作之前必须先进行写操作
writeThread1开始写入工作
writeThread2开始写入工作
writeThread3开始写入工作
writeThread1写入内容为:多线程知识点。写入工作结束!
writeThread2写入内容为:多线程CountDownLatch的知识点。写入工作结束!
writeThread3写入内容为:多线程中控制顺序可以使用CountDownLatch。写入工作结束!
读取第1条记录内容为:多线程知识点
读取第2条记录内容为:多线程CountDownLatch的知识点
读取第3条记录内容为:多线程中控制顺序可以使用CountDownLatch
读操作结束!

        从以上过程可以看出,可以使得先进行写操作然后进行读操作。

2.通过join实现CountDownLatch功能

        其实上述CountDownLatch这种功能可以通过Thread对象的join方法实现同样的功能,只是这里无须调用await()方法和countDown()方法,而是使用sleep()进行控制时间,然后将读操作以及写操作通过在主线程通过join()方法使其加入主线程,使其实现只有进行写操作结束,才能进行读操作。具体代码如下所示:

package concurrent;

import java.util.Vector;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class JoinDemo {
	private final static Vector v=new Vector();
	Lock lock=new ReentrantLock();
	final Condition condition=lock.newCondition();//创建condition对象
	
	private static class WriteThread extends Thread{
		private final String writeThreadName;
		private final int stopTime;
		private final String str;
		Lock lock=new ReentrantLock();
		final Condition condition=lock.newCondition();//创建condition对象
		public WriteThread(String name,int time,String str)
		{
			this.writeThreadName=name;
			this.stopTime=time;
			this.str=str;
		}
		public void run()
		{
			System.out.println(writeThreadName+"开始写入工作");
			try
			{
				Thread.sleep(stopTime);
			}
			catch(InterruptedException e)
			{
				e.printStackTrace();
			}
			
			v.add(str);
			System.out.println(writeThreadName+"写入内容为:"+str+"。写入工作结束!");
		}
	}
	private static class ReadThread extends Thread{
		Lock lock=new ReentrantLock();
		final Condition condition=lock.newCondition();//创建condition对象
		public void run()
		{
			System.out.println("读操作之前必须先进行写操作");
			try
			{
				Thread.sleep(10000);//该线程进行暂停,时间控制在写操作结束才使线程苏醒过来。
			}
			catch(InterruptedException e)
			{
				e.printStackTrace();
			}
			for(int i=0;i<v.size();i++)
			{
				System.out.println("读取第"+(i+1)+"条记录内容为:"+v.get(i));
			}
			System.out.println("读操作结束!");
		}
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		ReadThread readThread=new ReadThread();
		readThread.start();
		long start=System.currentTimeMillis();
		Thread[] write=new Thread[3];
		String[] str= {"多线程知识点","多线程CountDownLatch的知识点","多线程中控制顺序可以使用CountDownLatch"};
		

		for(int i=0;i<3;i++)
		{
			Thread t1= new WriteThread("writeThread"+(i+1),1000*(i+1),str[i]);
	           
	        
	       
	        t1.start();
	        write[i]=t1;
		}
		
		try
		{
			readThread.join();
		}
		catch(InterruptedException e)
		{
			e.printStackTrace();
		}
		
		
		//等待线程结束
		for(Thread t:write)
		{
			try
			{
				t.join();
				
			}
			catch(InterruptedException e)
			{
				e.printStackTrace();
			}

		}
		//等待线程结束
		
		

	}

}

运行上述程序,可以得到如下结果:

读操作之前必须先进行写操作
writeThread1开始写入工作
writeThread2开始写入工作
writeThread3开始写入工作
writeThread1写入内容为:多线程知识点。写入工作结束!
writeThread2写入内容为:多线程CountDownLatch的知识点。写入工作结束!
writeThread3写入内容为:多线程中控制顺序可以使用CountDownLatch。写入工作结束!
读取第1条记录内容为:多线程知识点
读取第2条记录内容为:多线程CountDownLatch的知识点
读取第3条记录内容为:多线程中控制顺序可以使用CountDownLatch
读操作结束!
3.线程池问题

        通过上述的比较,可以通过join方法实现CountDownLatch的按顺序执行线程的功能,但是CountDownLatch有join实现不了的情况,比如使用线程池时,线程池的线程不能直接使用,所以只能使用CountDownLatch实现按顺序执行线程,而无法使用join()方法。具体代码如下:

package concurrent;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import java.util.*;
public class CountDownLatchDemo {
	
	private final static CountDownLatch cdl=new CountDownLatch(3);
	private final static Vector v=new Vector();
	private final static ThreadPoolExecutor threadPool= new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());//使用线程池
	
	private static class WriteThread extends Thread{
		private final String writeThreadName;
		private final int stopTime;
		private final String str;
		public WriteThread(String name,int time,String str)
		{
			this.writeThreadName=name;
			this.stopTime=time;
			this.str=str;
		}
		public void run()
		{
			System.out.println(writeThreadName+"开始写入工作");
			try
			{
				Thread.sleep(stopTime);
			}
			catch(InterruptedException e)
			{
				e.printStackTrace();
			}
			cdl.countDown();
			v.add(str);
			System.out.println(writeThreadName+"写入内容为:"+str+"。写入工作结束!");
		}
	}
	private static class ReadThread extends Thread{
		public void run()
		{
			System.out.println("读操作之前必须先进行写操作");
			try
			{
				cdl.await();//该线程进行等待,直到countDown减到0,然后逐个苏醒过来。
				//Thread.sleep(3000);
			}
			catch(InterruptedException e)
			{
				e.printStackTrace();
			}
			for(int i=0;i<v.size();i++)
			{
				System.out.println("读取第"+(i+1)+"条记录内容为:"+v.get(i));
			}
			System.out.println("读操作结束!");
		}
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Thread read=new ReadThread();
		threadPool.execute(read);
		String[] str= {"多线程知识点","多线程CountDownLatch的知识点","多线程中控制顺序可以使用CountDownLatch"};
		

		for(int i=0;i<3;i++)
		{
			Thread t1= new WriteThread("writeThread"+(i+1),1000*(i+1),str[i]);
	           
	        
	       
	        threadPool.execute(t1);
	        
		}
		
		//new WriteThread("writeThread1",1000,"多线程知识点").start();
		//new WriteThread("writeThread2",2000,"多线程CountDownLatch的知识点").start();
		//new WriteThread("writeThread3",3000,"多线程中控制顺序可以使用CountDownLatch").start();

	}

}

运行如上程序,得到以下结果:

读操作之前必须先进行写操作
writeThread1开始写入工作
writeThread2开始写入工作
writeThread3开始写入工作
writeThread1写入内容为:多线程知识点。写入工作结束!
writeThread2写入内容为:多线程CountDownLatch的知识点。写入工作结束!
writeThread3写入内容为:多线程中控制顺序可以使用CountDownLatch。写入工作结束!
读取第1条记录内容为:多线程知识点
读取第2条记录内容为:多线程CountDownLatch的知识点
读取第3条记录内容为:多线程中控制顺序可以使用CountDownLatch
读操作结束!
4.总结

        CountDownLatch类主要是用来实现线程的按顺序执行。主要通过count计数器来实现功能,CountDownLatch(int count)构造函数用于初始化同步计时器,只有当同步计时器为0,主线程才会向下执行。而实现按顺序执行的两个主要方法便是await()和countDown(),其中await()使暂时不想让它执行的线程加入队列进入阻塞状态,而countDown()则是每当执行完一个可执行线程便会减1,直到count为0,那么阻塞队列的线程便会被唤醒。

        Thread对象的join方法可以实现相同的功能,但是特别地,当使用了线程池时,则join()方法便无法实现。但CountDownLatch依然可以实现功能。

            CountDownLatch类主要使用的场景有明显的顺序要求:比如只有等跑完步才能计算排名,只有等所有记录都写入才能进行统计工作等等,因此CountDownLatch完善的是某种逻辑上的功能,使得线程按照正确的逻辑进行。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值