• 并发是个什么鬼之同步工具类CountDownLatch


    扯淡

    写这篇文章,我先酝酿一下,实不相瞒,脱离底层太久了,更确切的情况是,真没曾认真研究过。就目前来说,很多框架包括工具类已经把实现封装的很深,你只需轻轻的调用一下API,便不费半点力气。以至于大家会产生种种错觉,开发无非如此,总觉得混个三五年,也能混上CTO,迎娶白富美,当然这毕竟是少数。

    我觉的大多数开发者的困惑是,底层的东西即使深入了解,如果不是BAT这种重研发型,自造血的公司,对于目前的工作亦或者将来的工作可能并没有什么用武之地。

    正题

    CountDownLatch 又是大神 Doug Lea的又一神作,正如每个Java文档所描述的那样,CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行。

    CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

    f65cc83b7b4664916fad5d1398a36005.png

    源码分析

    源码位于rt.java中的java.util.concurrent包中。

    1、CountDownLatch:A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

    中文解释:也就是说主线程在等待所有其它的子线程完成后再往下执行

    2、构造函数:CountDownLatch(int count)//初始化count数目的同步计数器,只有当同步计数器为0,主线程才会向下执行

    主要方法:void await()//当前线程等待计数器为0
    boolean await(long timeout, TimeUnit unit)//与上面的方法不同,它加了一个时间限制。
    void countDown()//计数器减1
    long getCount()//获取计数器的值

    3.它的内部有一个辅助的内部类:sync.
    它的实现如下:

    /**
         * Synchronization control For CountDownLatch.
         * Uses AQS state to represent count.
         */
        private static final class Sync extends AbstractQueuedSynchronizer {
            private static final long serialVersionUID = 4982264981922014374L;
    
            Sync(int count) {
                setState(count);
            }
    
            int getCount() {
                return getState();
            }
    
            protected int tryAcquireShared(int acquires) {
                return (getState() == 0) ? 1 : -1;
            }
    
            protected 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;
                }
            }
        }
    

    4.await()方法的实现

    public void await() throws InterruptedException {
       sync.acquireSharedInterruptibly(1);
    }
    public final void acquireSharedInterruptibly(int arg)
                throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
    		//调用3中的tryAcquireShared()方法
            if (tryAcquireShared(arg) < 0)
                doAcquireSharedInterruptibly(arg);//加入到等待队列中
        }
    

    5.countDown()方法的实现

    public void countDown() {
            sync.releaseShared(1);
    }
    public final boolean releaseShared(int arg) {
    //调用3中的tryReleaseShared()方法
            if (tryReleaseShared(arg)) {
    		//解锁
                doReleaseShared();
                return true;
            }
            return false;
    }
    

    应用场景

    比如主任务是一个比较复杂的运算,为了节约时间,我们可以拆分成多个子任务,多线程同时执行,最终统一汇总任务。

    其实生活也有类似的场景,比如马拉松赛跑,我们不可能按顺序依次跑步,这样得跑到猴年马月。一般来说都是大家听到发号命令一起跑,最终比赛结束,统一汇总成绩。

    代码案例

    下面看一个例子大家就清楚CountDownLatch的用法了:

    /**
     * CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待
     * 构造方法参数指定了计数的次数
     * countDown方法,当前线程结束执行后调用,计数减一 
     * awaint方法,调用此方法会一直阻塞当前线程,直到计时器的值为0
     * 创建者 科帮网
     * 创建时间	2017年8月15日
     *
     */
    public class CountDownLatchDemo {
    	final static SimpleDateFormat sdf = new SimpleDateFormat(
    			"yyyy-MM-dd HH:mm:ss");
    	
    	final static String startTime = sdf.format(new Date());
    
    	public static void main(String[] args) throws InterruptedException {
    		CountDownLatch latch = new CountDownLatch(2);// 两个赛跑者
    		Runer runer1 = new Runer("刘翔", 5000, latch);
    		Runer runer2 = new Runer("罗伯斯", 8000, latch);
    		runer1.start();//刘翔   开始跑步
    		runer2.start();//罗伯斯 开始跑步
    		latch.await();// 等待所有人赛跑结束
    		System.out.println("all runer done at " + sdf.format(new Date()));
    	}
    
    	static class Runer extends Thread {
    		String runerName;
    		int runTime;
    		CountDownLatch latch;
    
    		public Runer(String runerName, int runTime, CountDownLatch latch) {
    			this.runerName = runerName;
    			this.runTime = runTime;
    			this.latch = latch;
    		}
    
    		public void run() {
    			System.out.println("Runer " + runerName + " do run begin at "
    					+ startTime);
    			doRun();//跑步
    			System.out.println("Runer " + runerName + " do run complete at "
    					+ sdf.format(new Date()));
    			latch.countDown();// 终点结束,计数器减一
    
    		}
    
    		private void doRun() {
    			try {
    				Thread.sleep(runTime);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    
    }
    
  • 相关阅读:
    【LeetCode】241. Different Ways to Add Parentheses
    【LeetCode】240. Search a 2D Matrix II
    【LeetCode】239. Sliding Window Maximum
    【LeetCode】238. Product of Array Except Self
    【LeetCode】237. Delete Node in a Linked List
    【Babble】批量学习与增量学习、稳定性与可塑性矛盾的乱想
    【LeetCode】233. Number of Digit One
    【LeetCode】236. Lowest Common Ancestor of a Binary Tree
    MySQL存储过程
    mysql远程连接/访问速度慢的解决方案
  • 原文地址:https://www.cnblogs.com/smallSevens/p/7458949.html
Copyright © 2020-2023  润新知