1. 程式人生 > >Java併發程式設計(8)-使用閉鎖測試併發時執行緒安全性

Java併發程式設計(8)-使用閉鎖測試併發時執行緒安全性

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

        }
    }

在這裡插入圖片描述