CountDownLatch类模拟多线程并发获取一个单例对象

CountDownLatch类简介

先看看1.8源码里面对CountDownLatch的描述:

  • A synchronization aid that allows one or more threads to wait until

  • a set of operations being performed in other threads completes.

  • A {@code CountDownLatch} is initialized with a given count.

  • The {@link #await await} methods block until the current count reaches

  • zero due to invocations of the {@link #countDown} method, after which

  • all waiting threads are released and any subsequent invocations of

  • {@link #await await} return immediately. This is a one-shot phenomenon

  • – the count cannot be reset. If you need a version that resets the

  • count, consider using a {@link CyclicBarrier}.

  • A {@code CountDownLatch} is a versatile synchronization tool

  • and can be used for a number of purposes. A

  • {@code CountDownLatch} initialized with a count of one serves as a

  • simple on/off latch, or gate: all threads invoking {@link #await await}

  • wait at the gate until it is opened by a thread invoking {@link

  • #countDown}. A {@code CountDownLatch} initialized to N

  • can be used to make one thread wait until N threads have

  • completed some action, or some action has been completed N times.

  • A useful property of a {@code CountDownLatch} is that it

  • doesn’t require that threads calling {@code countDown} wait for

  • the count to reach zero before proceeding, it simply prevents any

  • thread from proceeding past an {@link #await await} until all

  • threads could pass.

  • Sample usage: Here is a pair of classes in which a group

  • of worker threads use two countdown latches:

  • The first is a start signal that prevents any worker from proceeding
  • until the driver is ready for them to proceed;

  • The second is a completion signal that allows the driver to wait
  • until all workers have completed.

英文有点长意思总结来说就是: 在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。 用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。

1. 准备工作线程

这里引用了源码里面的示例。目的是用来获取一个单例

public class Worker implements Runnable {
    private final CountDownLatch startSignal;
    private final CountDownLatch doneSignal;

    public Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
        this.startSignal = startSignal;
        this.doneSignal = doneSignal;
    }

    @Override
    public void run() {
        try {
            startSignal.await();//当前线程调用并阻塞在await()方法(直至startSignal为0)
            doWork();
            doneSignal.countDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    void doWork() {
        System.out.println("我是线程: " + Thread.currentThread().getName() + "   当前取得的单例为: " + Singleton.getInstance());
    }
}

2.创建测试类

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        int threadNum = 10;
        CountDownLatch startSingle = new CountDownLatch(1);
        CountDownLatch doneSingle = new CountDownLatch(threadNum);
        for (int i = 0; i < threadNum; i++) {
            new Thread(new Worker(startSingle, doneSingle)).start();
        }
        startSingle.countDown();//释放阻塞在startSignal.await()方法的线程,让他们开始执行
        System.out.println("开始释放所有线程");

        doneSingle.await();//阻塞主进程直至doneSingle为0,开始执行打印111
        System.out.println("执行主进程打印任务");
    }
}

3.准备单例类

3.1 线程不安全类(懒汉式)

public class Singleton {
    private Singleton() {
    }

    private static Singleton singleton = null;

    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }

打印如下

开始释放所有线程
我是线程: Thread-7   当前取得的单例为: com.dingshun.current.Singleton@3195835c
我是线程: Thread-1   当前取得的单例为: com.dingshun.current.Singleton@3195835c
我是线程: Thread-0   当前取得的单例为: com.dingshun.current.Singleton@3195835c
我是线程: Thread-5   当前取得的单例为: com.dingshun.current.Singleton@3195835c
我是线程: Thread-3   当前取得的单例为: com.dingshun.current.Singleton@5dd54814
我是线程: Thread-4   当前取得的单例为: com.dingshun.current.Singleton@3195835c
我是线程: Thread-8   当前取得的单例为: com.dingshun.current.Singleton@3195835c
我是线程: Thread-9   当前取得的单例为: com.dingshun.current.Singleton@3195835c
我是线程: Thread-6   当前取得的单例为: com.dingshun.current.Singleton@3195835c
我是线程: Thread-2   当前取得的单例为: com.dingshun.current.Singleton@3195835c
执行主进程打印任务

可见, 该单例模式的if null判断可能有多个线程跑进去,导致实例对象结果并不唯一,当10个线程并发获取单例对象,"单例"对象被示例化了两次

3.2 线程安全类(双重检测)

public class TwiceCheckSingleton {

    private static volatile TwiceCheckSingleton singleton;

    private TwiceCheckSingleton() {
    }

    public static TwiceCheckSingleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new TwiceCheckSingleton();
                }
            }
        }
        return singleton;
    }
}

在测试类中,把工作线程的dowork()方法改成获取TwiceCheckSingleton
打印如下

开始释放所有线程
我是线程: Thread-4   当前取得的单例为: com.dingshun.current.TwiceCheckSingleton@35d7e55a
我是线程: Thread-2   当前取得的单例为: com.dingshun.current.TwiceCheckSingleton@35d7e55a
我是线程: Thread-8   当前取得的单例为: com.dingshun.current.TwiceCheckSingleton@35d7e55a
我是线程: Thread-0   当前取得的单例为: com.dingshun.current.TwiceCheckSingleton@35d7e55a
我是线程: Thread-5   当前取得的单例为: com.dingshun.current.TwiceCheckSingleton@35d7e55a
我是线程: Thread-6   当前取得的单例为: com.dingshun.current.TwiceCheckSingleton@35d7e55a
我是线程: Thread-3   当前取得的单例为: com.dingshun.current.TwiceCheckSingleton@35d7e55a
我是线程: Thread-9   当前取得的单例为: com.dingshun.current.TwiceCheckSingleton@35d7e55a
我是线程: Thread-1   当前取得的单例为: com.dingshun.current.TwiceCheckSingleton@35d7e55a
我是线程: Thread-7   当前取得的单例为: com.dingshun.current.TwiceCheckSingleton@35d7e55a
执行主进程打印任务

这里的方法是线程安全的,无论执行多少次,每个线程获取的对象都是一样的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值