CountDownLatch基本使用及原理

2023-10-27

一、CountDownLatch简介

  1. CountDownLatch是一个线程同步工具类,它可以让一个或多个线程等待其他线程完成某个任务后再继续执行。它通过一个计数器来实现,计数器的初始值可以设置为一个正整数,每当一个线程完成任务后,计数器的值就会减1,当计数器的值变为0时,所有等待的线程就会被唤醒。

二、CountDownLatch类的继承关系

countdownlatch

1. AbstractQueuedSynchronizer: 用于构建锁和同步器的框架。

AbstractQueuedSynchronizer是一个用于构建锁和同步器的基类,它提供了一个框架,供子类重写并实现自己的同步逻辑。AQS内部维护了一个FIFO的等待队列,用于管理等待获取锁的线程,并提供了一些方法供子类操作这个队列。通过继承AQS,可以方便地实现各种同步机制。

2. Sync: CountDownLatch的内部类,提供了锁的具体实现。

Sync是CountDownLatch的内部类,它继承自AbstractQueuedSynchronizer(AQS),用于实现CountDownLatch的同步机制。Sync通过继承AQS,实现了AQS的共享模式,来控制多个线程之间的同步。

三、CountDownLatch的基本使用

1. 使用场景:

CountDownLatch 是 Java 中的一个同步工具类,可以用来实现线程之间的等待。在玩王者荣耀时,如果需要等待队友都准备好后才能开始游戏,可以使用 CountDownLatch 来实现。
wangzhe

2. 代码实现:

  1. 首先,创建一个 CountDownLatch 对象,将其计数器初始化为队友的数量。每当一个队友准备好后,调用 CountDownLatch 的 countDown() 方法来减少计数器的值。

  2. 主线程调用 CountDownLatch 的 await() 方法,将会阻塞在这里,直到计数器的值为 0,即所有队友都准备好了。

  3. 以下是一个简单的示例代码:

import java.util.concurrent.CountDownLatch;

public class PlayGame {
    public static void main(String[] args) throws InterruptedException {
        int numOfTeammates = 5; // 队友的数量

        // 创建一个 CountDownLatch 对象,计数器初始值为队友的数量
        CountDownLatch latch = new CountDownLatch(numOfTeammates);

        // 模拟队友准备的过程
        for (int i = 0; i < numOfTeammates; i++) {
            new Thread(() -> {
                try {
                    // 模拟队友准备的时间
                    Thread.sleep((long) (Math.random() * 5000));
                    System.out.println(Thread.currentThread().getName() + " 准备好了");
                    // 队友准备好后,调用 countDown() 方法减少计数器的值
                    latch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }

        System.out.println("等待所有队友准备好...");
        // 主线程调用 await() 方法等待计数器的值为 0
        latch.await();
        System.out.println("所有队友都准备好了,开始游戏!");
    }
}

3. 运行结果:

yunxing

4. 案例分析:

  1. 在上面的示例代码中,首先创建了一个 CountDownLatch 对象,并将计数器初始化为队友的数量。然后,使用多线程模拟队友准备的过程,每个线程准备好后调用 countDown() 方法减少计数器的值。

  2. 最后,主线程调用 await() 方法等待计数器的值为 0,即所有队友都准备好了,然后输出相应的提示信息,表示可以开始游戏。

  3. 注意:CountDownLatch 是一次性的,计数器的值减少到 0 后,无法再次使用。如果需要多次等待,可以考虑使用 CyclicBarrier 类。

四、CountDownLatch的优缺点

1. 优点:

CountDownLatch 是 Java 并发包中的一个工具类,用于控制多个线程的执行顺序。它的主要优点是:

1.1、简单易用:

CountDownLatch 提供了一种简单的机制,可以让一个或多个线程等待其他线程完成某个操作后再继续执行。它的使用方法非常简单,只需在需要等待的线程中调用 await() 方法即可。

1.2、灵活性高:

CountDownLatch 可以用于任意数量的线程等待,而且可以在任意时刻进行倒数计数,不需要事先确定等待线程的数量。

1.3、线程安全:

CountDownLatch 内部使用了多线程并发控制的机制,保证了线程安全性。

2. 缺点:

然而,CountDownLatch 也存在一些缺点:

2.1、只能倒数一次:

CountDownLatch 的计数器只能倒数一次,一旦计数器归零,就无法重置。如果需要重复使用倒数计数功能,就需要重新创建一个新的 CountDownLatch。

2.2、不支持中断:

CountDownLatch 的 await() 方法在等待时无法被中断,只能等倒数计数完成或者等待超时才能退出。

2.3、异常处理困难:

在使用 CountDownLatch 时,如果等待的线程发生异常导致无法完成计数操作,其他线程将一直等待下去,没有异常处理机制。

3. 小结:

总体来说,CountDownLatch 是一种简单有效的线程同步工具,但在某些特定情况下可能不够灵活,需要结合其他并发工具来解决问题。

五、底层原理分析

CountDownLatch是Java多线程中的一个同步工具类,它可以让一个或多个线程等待其他线程完成操作后再继续执行。CountDownLatch类提供了两个重要的方法:await()和countDown()。

1. await()方法:

  • await()方法用于使当前线程等待,直到计数器的值变为0。
  • 在调用await()方法之前,需要通过构造函数指定计数器的初始值。
  • 当一个线程调用await()方法后,如果计数器的值大于0,则该线程会被阻塞,直到计数器的值减到0为止。
  • 当计数器的值为0时,await()方法将立即返回,并且所有等待的线程将被唤醒。

2. countDown()方法:

  • countDown()方法用于将计数器的值减1。
  • 每次调用countDown()方法,计数器的值都会减1。
  • 当计数器的值减到0时,await()方法中阻塞的线程将被唤醒。
  • 下面是CountDownLatch的部分源码分析:
public class CountDownLatch {
private final Sync sync;

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

public void await() throws InterruptedException {
	sync.acquireSharedInterruptibly(1);
}

public void countDown() {
	sync.releaseShared(1);
}

private static final class Sync extends AbstractQueuedSynchronizer {
	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;
    	}
	}
}
}
  • 在CountDownLatch的源码中,Sync是一个继承了AbstractQueuedSynchronizer的内部类,它实现了具体的计数逻辑。在Sync中,使用了一个整数来表示计数器的值。在构造函数中,通过setState(count)来初始化计数器的值。tryAcquireShared()方法用于判断计数器是否已经减到0,如果是则返回1,表示可以获取共享资源;如果不是则返回-1,表示无法获取共享资源。tryReleaseShared()方法用于减少计数器的值,并判断是否减到0,如果是则返回true,表示可以唤醒等待的线程;如果不是则返回false。

  • 在await()方法中,调用了sync.acquireSharedInterruptibly(1)方法,该方法会调用Sync中的tryAcquireShared()方法。如果计数器的值不为0,则当前线程会被阻塞,直到计数器的值减到0为止。

  • 在countDown()方法中,调用了sync.releaseShared(1)方法,该方法会调用Sync中的tryReleaseShared()方法。每次调用countDown()方法,计数器的值都会减1。当计数器的值减到0时,tryReleaseShared()方法会返回true,表示可以唤醒等待的线程。

  • 通过上述分析,我们可以看出CountDownLatch的await()方法和countDown()方法是通过对计数器的操作来实现线程的等待和唤醒。当计数器的值减到0时,await()方法中阻塞的线程将被唤醒,继续执行后续的操作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CountDownLatch基本使用及原理 的相关文章

随机推荐