`
snake1987
  • 浏览: 71883 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

java并发学习之一:CountDownLatch

    博客分类:
  • java
阅读更多
看了几个月的《Java Concurrency in Practice》到了今天终于算可以收尾了,之前留下的看不懂的代码,现在也基本明晰了一些

全书介绍了很多细节问题,很多注意的点,很多原则性问题,个人感觉,无论看几遍,都是值得的。但很多都是一些需要去记忆的东西,这个是需要经验的积累的。
真正想在思考上,在设计上得到更大的提高,看来必然是要落在了concurrent包的数个同步器的实现的分析和对AQS的理解上了

同步器是针对一些通用的场景设计的,由Doug Lea实现的,换句话来说,其实从同步器可以看出常用的一些需求,既然有了需求,又学会了工具(AQS框架)的使用,就可以按照自己的思维,试着重新去实现一下了


CountDownLatch

CountDownLatch是针对这样一个需求设计的:n个参与者需要进行一件事情(比如开会),只有当他们都到了,才能正式开始,所以先到的需要等,并且同时开始

首先,先用点“拙劣”的工具(1.5之前的实现方法)试着写一下实现
public class TestOfCountDownLatches {
	
	int count;
	
	Object lock = new Object();
	
	public TestOfCountDownLatches(int count)
	{
		this.count = count;
	}
	
	public void countDown()
	{
		synchronized(lock)
		{
			count--;
			if(count == 0)
			{
				lock.notifyAll();
			}
		}
	}

        public void await() throws InterruptedException
	{
		synchronized(lock)
		{
//中间还出现了点小问题:没写if(count == 0),这就导致了如果主线程先执行了countdown,其他线程再执行await,就会无限制的等待着了,从这个小错误也学会了并发框架的一个原则:阻塞还是不阻塞,应该是严格依赖于状态(state)的,而与先后次序没关系
			if(count != 0)
				lock.wait();
		}
	}
}


测试方法:
public long timeTasks1(int nThreads,final Runnable task)
		{
			final TestOfCountDownLatches endLatch = new TestOfCountDownLatches(nThreads);
			final TestOfCountDownLatches startLatch = new TestOfCountDownLatches(1);
			for(int i = 0;i<nThreads;i++)
			{
				Thread t = new Thread(){
					public void run() {
	                    try {
	                    	startLatch.await();
	                        try {
	                            task.run();
	                        } finally {
	                        	endLatch.countDown();
	                        }
	                    } catch (InterruptedException ignored) { }
	                }
				};
				t.start();
			}
			long start = System.nanoTime();			
			try {
				startLatch.countDown();
				endLatch.await();
			} catch (InterruptedException e) {
				
			}
			long end = System.nanoTime();
			return end-start;
		}


跑了一下,没啥问题,与CountDownLatches的功能基本一致的

再看看CountDownLatches的实现(主要是看它的同步器)
private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

        Sync(int count) {
            setState(count);
        }

        int getCount() {
            return getState();
        }
//被await()调用
        public int tryAcquireShared(int acquires) {
            return getState() == 0? 1 : -1;
        }
//被countDown()调用
        public boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }


在实现原理上是一致的,await时比较一下state(相当于第一个版本的count),如果满足了,则不阻塞(返回1),如果不满足,阻塞(返回-1)(关于阻塞与否的代码都在AQS里面)
在countDown时则进行一下--操作,如果满足0“return nextc == 0;”了,则释放阻塞

既然主要目的是学习AQS,就debug了一下,尽量去了解AQS的实现原理
主要了解到以下几个方面:(下面的描述只针对AQS的shared模式)
1.AQS没用正常的wait,notifyAll,lock的阻塞等方法,而是用了一个LockSupport对象来支持类似操作,该对象的方法全是本地方法,相当于对synchronized和wait等操作用了一个统一的形式
2.AQS中维持了一个等待队列,很奇特的是,该等待队列“完全没用锁”,比如说一个acquice操作,当没成功的时候,是需要将该线程进入等待队列的,多线程的访问,所以这个入队的过程应该要保证它的原子性
但AQS没有这样做,它只用了一些非常高效的改变int型的原子方法(由Unsafe提供),针对每一个需要保证原子性的操作(如入队),它都用了一个while(true)这样的形式,一旦失败(比如发现队尾已经被别的线程改变了),则从新获取当前状态,重新入队,很神奇,很高效,同时也需要考虑得面面俱到的一个实现,在文章中称之为“非阻塞算法”,在后文会对该算法写一篇详细介绍
虽然用1.5之前的wait等方法可以实现,但无疑用AQS这套框架会更高效,响应更快

再贴一个使用CountDownLatches的例子做为结尾
public long timeTasks2(int nThreads, final Runnable task)
	            throws InterruptedException {
	        final CountDownLatch startGate = new CountDownLatch(1);
	        final CountDownLatch endGate = new CountDownLatch(nThreads);

	        for (int i = 0; i < nThreads; i++) {
	            Thread t = new Thread() {
	                public void run() {
	                    try {
	                        startGate.await();
	                        try {
	                            task.run();
	                        } finally {
	                            endGate.countDown();
	                        }
	                    } catch (InterruptedException ignored) { }
	                }
	            };
	            t.start();
	        }

	        long start = System.nanoTime();
	        startGate.countDown();
	        endGate.await();
	        long end = System.nanoTime();
	        return end-start;
	    }
分享到:
评论

相关推荐

    java并发工具类(CountDownLatch+Semaphore+Exchanger)

    java并发工具类(CountDownLatch+Semaphore+Exchanger);java并发工具类(CountDownLatch+Semaphore+Exchanger);java并发工具类(CountDownLatch+Semaphore+Exchanger);java并发工具类(CountDownLatch+...

    Java并发编程:CountDownLatch与CyclicBarrier和Semaphore的实例详解

    主要介绍了Java并发编程:CountDownLatch与CyclicBarrier和Semaphore的实例详解的相关资料,需要的朋友可以参考下

    阿里Java并发程序设计教程

    7、并发流程控制手段:CountDownlatch、Barrier 8、定时器: ScheduledExecutorService、大规模定时器TimerWheel 9、并发三大定律:Amdahl、Gustafson、Sun-Ni 10、神人和图书、相关网络资源 11、业界发展情况: GPGPU...

    Java并发程序设计教程

    7、并发流程控制手段:CountDownlatch、Barrier 8、定时器: ScheduledExecutorService、大规模定时器TimerWheel 9、并发三大定律:Amdahl、Gustafson、Sun-Ni 10、神人和图书 11、业界发展情况: GPGPU、OpenCL 12、...

    Java编程并发程序设计

    7、并发流程控制手段:CountDownlatch、Barrier 8、定时器: ScheduledExecutorService、大规模定时器TimerWheel 9、并发三大定律:Amdahl、Gustafson、Sun-Ni 10、神人和图书 11、业界发展情况: GPGPU、OpenCL 12、...

    java并发编程中CountDownLatch和CyclicBarrier的使用借鉴.pdf

    java并发编程中CountDownLatch和CyclicBarrier的使用借鉴.pdf

    Java并发编程一CountDownLatch、CyclicBarrier、Semaphore初使用

    Java并发编程一CountDownLatch、CyclicBarrier、Semaphore初使用 CountDownLatch、CyclicBarrier、Semaphore这些线程协作工具类是基于AQS的,看完这篇博客后可以去看下面这篇博客,了解它们是如何实现的。 Java并发...

    龙果java并发编程完整视频

    第4节学习并发的四个阶段并推荐学习并发的资料 [免费观看] 00:09:13分钟 | 第5节线程的状态以及各状态之间的转换详解00:21:56分钟 | 第6节线程的初始化,中断以及其源码讲解00:21:26分钟 | 第7节多种创建线程的...

    java并发编程中CountDownLatch和CyclicBarrier的使用.pdf

    java并发编程中CountDownLatch和CyclicBarrier的使用.pdf

    基础技术部牛路《Java多线程入阶分享》纯干货

    Java多线程入阶干货分享 1.使用线程的经验:设置名称、响应中断、...7.并发流程控制手段:CountDownLatch、Barrier 8.定时器:ScheduledExecutorService、大规模定时器TimerWheel 9.并发三大定律 10.图书、相关网络资源

    龙果 java并发编程原理实战

    龙果 java并发编程原理实战 第2节理解多线程与并发的之间的联系与区别 [免费观看] 00:11:59分钟 | 第3节解析多线程与多进程的联系以及上下文切换所导致资源浪费问题 [免费观看] 00:13:03分钟 | 第4节学习并发的四...

    Java并发编程学习笔记

    7、并发工具类CountDownLatch 、CyclicBarrier和Semaphore底层实现原理 8、线程池原理和如何使用线程池 9、ThreadLocal 为什么会内存泄漏 10、Volatile底层实现原理 11、AQS源码分析 12、CAS原理分析和使用场景 13、...

    java并发工具包 java.util.concurrent中文版用户指南pdf

    1. java.util.concurrent - Java 并发工具包 2. 阻塞队列 BlockingQueue 3. 数组阻塞队列 ArrayBlockingQueue 4. 延迟队列 DelayQueue 5. 链阻塞队列 LinkedBlockingQueue 6. 具有优先级的阻塞队列 ...

    java并发之并发工具类

    java并发之并发工具类,并发工具类有很多,这里主要介绍了CyclicBarrier、CountDownLatch、Semaphore、Exchanger

    java并发工具包详解

    1. java.util.concurrent - Java 并发工具包 2. 阻塞队列 BlockingQueue 3. 数组阻塞队列 ArrayBlockingQueue 4. 延迟队列 DelayQueue 5. 链阻塞队列 LinkedBlockingQueue 6. 具有优先级的阻塞队列 ...

    Java并发编程原理与实战

    并发工具类CountDownLatch详解.mp4 并发工具类CyclicBarrier 详解.mp4 并发工具类Semaphore详解.mp4 并发工具类Exchanger详解.mp4 CountDownLatch,CyclicBarrier,Semaphore源码解析.mp4 提前完成任务之FutureTask...

    Java 并发编程原理与实战视频

    java并发编程原理实战 第2节理解多线程与并发的之间的联系与区别 [免费观看] 00:11:59分钟 | 第3节解析多线程与多进程的联系以及上下文切换所导致资源浪费问题 [免费观看] 00:13:03分钟 | 第4节学习并发的四个...

    Java并发编程(学习笔记).xmind

    CountDownLatch:可以使一个或多个线程等待一组事件发生 FutureTask *应用场景 (1)用作异步任务使用,且可以使用get方法获取任务的结果 (2)用于表示一些时间较长的计算 状态 ...

Global site tag (gtag.js) - Google Analytics