`
zhangwei_david
  • 浏览: 476077 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Java 多线程之闭锁-CountDownLatch

 
阅读更多

    实现并发的最直接方式是在操作系统级别使用进程。进程是运行在自己的地址空间内的自包容程序。多任务操作系统可以通过周期性地将CPU从一个进程切换到另一个进程,来实现同时运行多个进程的。操作系统将进程相互隔离开,因此他们不会相互干扰,这使得通过进程实现并发编程相对容易一些。而JAVA的并发时通过多线程机制实现的。

   一个线程就是在进程中的一个单一的顺序控制流,因此,单个进程可以拥有多个并发执行的任务,使得程序看起来好像都有其自己的CPU一样。其底层的机制是切分CPU的时间片。

    java.util.concurrency包提供了很多并发编程的工具类,可以极大提高并发编程的效率。

 

   闭锁是一种同步工具类,可以延迟线程的进度直到闭锁到达终止状态。Latch在英语中就是门栓的意思,所以形象地说闭锁就相当于一扇门,在日常生 活中我们都遇到过类似的场景,进入一个场馆前,必须达到一定的条件,比如活动开始前半小时可以入场;如果来早了的话,对不起,以便等着。 CountDownLatch 在多线程中也是这样的作用,在闭锁到达结束状态前,这扇门是一直关闭的,不允许任何线程通过,当到达结束状态时,这扇门就 保持打开,并且是永久的处于打开状态;也就是说这个门是一次性的。 如同柏林墙一样,推到了就建不起来了。

     闭锁可以用来确保某些活动直到其他活动都完成后才继续执行。CountDownLatch是一种灵活的闭锁实现,它可以使一个或多个线程 等待一组事件的发生。闭锁包括一个计数器,该计数器被初始化为一个正整数,表示等待的事件数量。有一等待时间发生时,使用countDown方法递减计数 器,而await方法就是等待所有事件的发生,也就是计数器的值为0.如果计数器的值不为0 那么await 方法需要一直等待。

 

public class CountDownLatch {
    /**
     * 闭锁的同步控制类
     */
    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;
		// 
        Sync(int count) {
            setState(count);
        }

        int getCount() {
            return getState();
        }
		//获取共享锁,如果当前线程数量为0则表示获取到锁,否则无法获取
        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }
		//释放共享锁
        protected boolean tryReleaseShared(int releases) {
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }

    private final Sync sync;

    /**
     * 构造器
     */
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

    /**
     *  当前线程一直等到闭锁的数量为0才可以执行
     */
    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

    /**
     * 当前线程等到闭锁的数量为0或者超时
     */
    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

    /**
     * 闭锁减去1
     */
    public void countDown() {
        sync.releaseShared(1);
    }

    /**
     * 获取当前闭锁的数量
     */
    public long getCount() {
        return sync.getCount();
    }

   
    public String toString() {
        return super.toString() + "[Count = " + sync.getCount() + "]";
    }
}

 AbstractQueuedSynchronizer#acquireSharedInterruptibly()方法,首先调用子类的实现方法判断是否可以获取到锁,如果无法获取到锁,则调用doAcquireAsharedInterruptibly()方法

   public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

 

 

 /**
     *.
     *在中断模式中获取共享锁
     */
    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
		//将当前线程添加到等待队列中
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
			//循环
            for (;;) {
			     //获取队列中的上一个节点
                final Node p = node.predecessor();
				//如果上一个节点是队列头
                if (p == head) {
					//视图获取共享锁
                    int r = tryAcquireShared(arg);
					//获取到锁
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

 

 

/**
 *
 * @author zhangwei_david
 * @version $Id: TestHarness.java, v 0.1 2014年11月10日 下午5:08:17 zhangwei_david Exp $
 */
public class TestHarness {

    public long timeTasks(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() {
                @Override
                public void run() {
                    try {
                        // 等待所有线程都准备好
                        startGate.await();
                        try {
                            task.run();
                        } finally {
                            // 线程执行结束,闭锁减一
                            endGate.countDown();
                        }
                    } catch (InterruptedException e1) {
                    }
                }
            };
            t.start();
        }
        long start = System.nanoTime();
        //// 所有的线程都准备好,可以执行
        startGate.countDown();
        // 等待所有线程执行结束
        endGate.await();

        long end = System.nanoTime();
        return end - start;
    }
}

 

/**
 *
 * @author zhangwei_david
 * @version $Id: Test.java, v 0.1 2014年11月10日 下午5:13:24 zhangwei_david Exp $
 */
public class Test {
    public static void main(String[] args) throws InterruptedException {

        System.out.println(new TestHarness().timeTasks(10, new Runnable() {

            public void run() {
                System.out.println(Thread.currentThread().getName() + "开始执行时间:"
                                   + System.currentTimeMillis());
            }
        }));
    }
}

 

   结果是:

Thread-2开始执行时间:1435904413836
Thread-9开始执行时间:1435904413836
Thread-5开始执行时间:1435904413836
Thread-7开始执行时间:1435904413836
Thread-6开始执行时间:1435904413836
Thread-3开始执行时间:1435904413836
Thread-8开始执行时间:1435904413836
Thread-1开始执行时间:1435904413836
Thread-0开始执行时间:1435904413836
Thread-4开始执行时间:1435904413836
1192112

 通过console控制台输出的结果可以看出:所有的线程几乎是在同一时刻开始执行的,这个就是闭锁的作用!

4
3
分享到:
评论
1 楼 toknowme 2015-07-03  
闭锁还可以用于所有线程一起结束

http://toknowme.iteye.com/blog/2210399

相关推荐

    mybaits 多线程 实现数据批量插入 (运用CountDownLatch实现闭锁)

    本文将详细介绍如何利用MyBatis结合多线程和CountDownLatch闭锁来实现数据的批量插入。 首先,我们来看`mybatis批处理`。MyBatis的批处理功能允许我们在一次数据库连接中执行多条SQL语句,从而减少了数据库连接的...

    Java多线程的调度_动力节点Java学院整理

    Java多线程调度方法 在 Java 中,多线程的调度是指控制多个线程执行的先后次序。下面将介绍四种常见的 Java 多线程调度方法。 方法一:设置线程优先级 Java.lang.Thread 提供了 setPriority(int newPriority) ...

    并发编程之CountDownLatch

    在实际开发中,CountDownLatch 可以用来实现各种并发编程的场景,例如在多线程环境下同时启动多个线程,或者在某些特定的时刻同时启动多个线程等。下面是一个使用 CountDownLatch 实现主线程和子线程通信的示例代码...

    Java并发编程之闭锁与栅栏的实现

    在Java并发编程中,闭锁和栅栏是两种重要的同步工具,它们可以帮助开发者在多线程环境下控制线程的执行顺序和同步。本篇文章将详细解释这两种机制,并通过实例代码进行演示。 一、闭锁(CountDownLatch) 闭锁,由...

    J.U.C-AQS框架同步组件之闭锁CountDownLatch介绍

    CountDownLatch的一个典型应用场景是在多线程测试或者多线程初始化场景中,例如在测试中,主线程可能需要等待所有测试线程执行完毕后再进行结果收集;在框架服务启动时,主线程可能需要等待所有服务启动线程完成启动...

    实例讲解Java并发编程之闭锁

    这段代码演示了`CountDownLatch`如何在多线程环境中同步任务,确保特定操作的顺序。`CountDownLatch`是一种强大的工具,常用于初始化、并发测试以及协调复杂的并发操作。它提供了灵活的方式来控制线程间的依赖关系,...

    多线程编程.docx

    ### 多线程编程知识点详解 #### 一、Java.util.concurrent包简介 Java.util.concurrent包是专门为Java并发编程设计的,...以上知识点涵盖了Java多线程编程的核心概念和技术,对于理解和应用Java并发编程具有重要意义。

    Java并发编程基础.pdf

    - 是线程安全的哈希表实现,通过分段加锁的方式保证了在多线程环境下的安全性。 - 性能高于传统的`synchronized` Map,适用于高并发场景。 - **CopyOnWriteArrayList**: - 一种特殊的列表实现,写入时复制,即...

    Java并发系列之CountDownLatch源码分析

    Java并发系列之CountDownLatch源码分析 CountDownLatch是一种非常有用的工具类,用于拦截一个或多个线程,使其在某个条件成熟后再执行。它的内部提供了一个计数器,在构造闭锁时必须指定计数器的初始值,且计数器的...

    CountDownLatch、Semaphore等4大并发工具类详解

    应用场景:多线程协作,例如多个线程协作完成某个任务。 Phaser Phaser 是一个更加灵活的同步工具类,可以用来实现复杂的同步逻辑。Phaser 可以注册多个 partiecipants,每个 partiicipant 可以在 Phaser 中注册,...

    深入学习Java同步机制中的底层实现

    `CountDownLatch`则是一种闭锁,常用于多线程间的协调,它允许一个或多个线程等待其他线程完成操作。`CountDownLatch`的`Sync`子类同样基于AQS,通过`tryAcquireShared`和`tryReleaseShared`来实现计数器的递减与...

    并发编程实践,全面介绍基础知识、JVM同步原语、线程安全、低级并发工具、线程安全容器、高级线程协作工具、Executor部分等

    - **闭锁**(CountDownLatch):一次性计数器,用于等待其他线程完成操作。 - **栅栏**(CyclicBarrier):一组线程达到某个点时才继续执行。 - **Fork/Join框架**:用于解决大问题的分割和并行处理。 7. **...

    深入浅出_Java并发工具包原理讲解

    3. 各种并发工具类:包括闭锁(CountDownLatch)、栅栏(CyclicBarrier)、信号量(Semaphore)、读写锁(ReentrantReadWriteLock)等,这些工具类提供了丰富的线程间协调和控制的机制,使得多线程之间的合作更加...

    java经典面试题

    - `CountDownLatch`:倒计时闭锁。 28. **`wait()` 和 `sleep()` 的区别**: - `wait()` 会释放对象的锁,而 `sleep()` 不会。 - `wait()` 需要在同步上下文中使用,而 `sleep()` 可以在任何地方使用。 29. **`...

    Java并发编程之栅栏(CyclicBarrier)实例介绍

    与闭锁(CountDownLatch)不同,闭锁通常是一次性的,而CyclicBarrier可以重用,即“循环”在每次所有线程都到达屏障点后重新初始化。 1. **CyclicBarrier的基本概念**: - **屏障点**:CyclicBarrier的核心是屏障...

    Java判断线程池线程是否执行完毕

    Java判断线程池线程是否执行完毕是Java多线程编程中的一种常见问题,当我们使用线程池来执行多个任务时,需要判断所有的子线程是否已经执行完毕,以便进行后续操作。下面我们将介绍两种解决方案:使用Semaphore和...

    JAVA中锁的解决方案.docx

    在 JAVA 中,锁是非常重要的概念,它可以帮助开发者解决多线程编程中出现的同步问题。在本文中,我们将深入介绍 JAVA 中锁的解决方案,包括乐观锁和悲观锁的定义、核心代码剖析以及使用场景。 一、乐观锁 乐观锁是...

    Java中同步与并发用法分析

    在Java编程中,同步与并发是多线程编程的核心概念,它们主要用于解决多个线程间的数据竞争和资源争抢问题,以确保程序的正确性和高效性。以下是对这些概念的详细分析: 1. **同步容器类** - **Vector** 和 **...

Global site tag (gtag.js) - Google Analytics