Java併發編程(9)-使用閉鎖測試併發線程安全性


本文將介紹什麼是閉鎖,在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();

        }
    }

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章