Java并发编程(9)-使用闭锁测试并发线程安全性

版权声明: https://blog.csdn.net/pbrlovejava/article/details/83753605


更多关于Java并发编程的文章请点击这里:Java并发编程实践(0)-目录页


本文将介绍什么是闭锁,在java中的闭锁实现:CountDownLatch类及其常用方法等,最后给出了一个使用闭锁模拟线程并发的demo,用以简单地测试任务是否为线程安全。

一、什么是闭锁

闭锁(Latch)是在并发编程中常被提及的概念。闭锁是一种线程控制对象,它能让所有的线程在某个状态时终止工作并等待,直到闭锁“开门”时,所有的线程在这一刻会几乎同时执行工作,制造出一个并发的环境。

二、CountDownLatch类介绍

2.1、什么是CountDownLatch

CountDownLatch,顾名思义,可以理解为计数(count)、减少(down)、闭锁(Latch),即通过计数减少的方式来达到阻碍线程执行任务的一种闭锁,这个类位于java.util.concurent并发包下,是java中闭锁的最优实现。

2.2、构造方法

CountDownLatch(int count)
构造器中计数值(count)就是闭锁需要等待的线程数量,这个值只能被设置一次。

2.3、主要方法

  • void await():
    使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。

  • boolean await(long timeout, TimeUnit unit):
    使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。

  • void countDown():
    递减锁存器的计数,如果计数到达零,则释放所有等待的线程。

  • long getCount():
    返回当前计数。

  • String toString():
    返回标识此锁存器及其状态的字符串。

三、使用闭锁完成并发测试

使用闭锁完成并发测试的基本思路是,定义一个startLatch闭锁,并且它的线程等待值设置为1;之后新建的每一个线程都需要在执行任务前都在这个startLatch下等待,等所有线程都已集合完毕后,释放startLatch,让所有的线程都执行任务。

  • 测试非线程安全的程序:
 private int count_unsafe = 0;

    class MyTask_Unsafe implements Runnable {


        @Override
        public void run() {
            count_unsafe++;
            System.out.println(Thread.currentThread().getName() + "读数为" + count_unsafe);
        }

    }

        @Test
        public void testConcurrent() {
            //新建闭锁
            CountDownLatch startLatch = new CountDownLatch(1);
            //模拟100个线程去并发执行任务
            for (int i = 0; i < 100; i++) {
                new Thread() {
                    public void run() {
                        try {
                            //线程工作到此处即停止
                            startLatch.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                        //在闭锁允许后,调用任务
                        new MyTask_Unsafe().run();
                    }
                }.start();
            }

            //100次循环后,100个线程已经创建并且在等待中,可以统一开始执行任务
            System.out.println("所有线程集合完毕,等待执行任务...");
            //开始执行任务
            startLatch.countDown();

        }
    }

执行结果为:在这里插入图片描述
可以发现,有3个线程输出的数字都为7,原因在第一篇文章中已经讲过,count_unsafe++这个操作并不是原子的,而是存在读-改-写三个过程,所以线程不安全,下面我们测试一下线程安全的任务:

 private AtomicInteger count = new AtomicInteger(0);

    class MyTask_Safe implements Runnable {

        //线程安全类
        @Override
        public void run() {
            count.incrementAndGet();
            System.out.println(Thread.currentThread().getName() + "读数为" + count);
        }

    }
        @Test
        public void testConcurrent() {
            //新建闭锁
            CountDownLatch startLatch = new CountDownLatch(1);
            //模拟100个线程去并发执行任务
            for (int i = 0; i < 100; i++) {
                new Thread() {
                    public void run() {
                        try {
                            //线程工作到此处即停止
                            startLatch.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                        //在闭锁允许后,调用任务
                        new MyTask_Safe().run();
                    }
                }.start();
            }

            //100次循环后,100个线程已经创建并且在等待中,可以统一开始执行任务
            System.out.println("所有线程集合完毕,等待执行任务...");
            //开始执行任务
            startLatch.countDown();

        }
    }

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/pbrlovejava/article/details/83753605