(三)線程同步工具集_1---控制線程併發訪問一個資源

線程同步工具集

在前面瞭解了線程的同步機制,臨界區等,瞭解了線程的兩種基本的同步機制:java

  • synchronized關鍵字;
  • Lock接口以及其實現類:ReentrantLock,ReentrantReadWriteLock.ReadLock,ReentrantLock.WriteLock

在接下來將要了解到的是更高級的同步機制,主要有如下幾種:編程

  • Semaphores(信號量):經過計數來控制共享資源的訪問,該機制在大多數編程語言中都提供了;
  • CountDownLatch(閉鎖):該機制容許線程等待多個操做終止;
  • CyclicBarrier(關卡):該機制容許多個線程在同一個點同步;
  • phaser(階段):該機制將線程同步任務分紅多個階段,全部的線程必須按照階段順序一個個的完成;該特性是Java 7 的新特性;
  • Exchanger(交換):該機制提供了多個線程交換的一個數據點;

信號量(semaphore)機制是一個通用的線程同步機制,而其它幾個線程同步工具須要根據本身的應用場景選擇符合其特性的同步機制;dom

信號量機制其實就是使用一個內部計數器,當該數值大於0時,代表還有能夠訪問的共享資源,當一個線程進入到臨界區時,計數器就會減一,當數值爲0時,表示已經沒有能夠訪問的共享資源了,在有線程進來訪問將會被掛起;semaphore構造函數中能夠傳入一個數值,也能夠稱爲」許可「,當該數值爲1時,一般稱爲」binary semphore",由於只有0 和 1 ;編程語言

下面的簡單示例中展現瞭如何使用semaphores同步共享數據;ide

動手實現

(1)建立一個PrintQueue函數

public class PrintQueue {
    private final Semaphore semaphore;

    public PrintQueue() {
        // binary semaphore
        this.semaphore = new Semaphore(1);
    }

    public void printJob(){
        try {
            semaphore.acquire();
            long duration=(long)(Math.random()*10);
            System.out.printf("%s: PrintQueue: Printing a Job during %d seconds\n",
                    Thread.currentThread().getName(),duration);
            Thread.sleep(duration);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            semaphore.release();
        }
    }
}

(2)建立一個執行線程

public class Job implements Runnable {
    private PrintQueue printQueue;

    public Job(PrintQueue printQueue) {
        this.printQueue = printQueue;
    }

    @Override
    public void run() {
        System.out.printf("%s: Going to print a job\n",
                Thread.currentThread().getName());
        printQueue.printJob();
        System.out.printf("%s: The document has been printed\n",
                Thread.currentThread().getName());
    }
}
(3)Main

public class Main {
    public static void main(String[] args) {
        PrintQueue printQueue=new PrintQueue();
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(new Job(printQueue), "Thread_" + i);
        }
        for (int i = 0; i < 10; i++) {
            threads[i].start();
        }
    }
}

要點

當進入同步代碼塊時
1.首先執行 acquire()獲取鎖
2.執行相應業務操做
3.執行release()方法,釋放鎖;
該方法的構造函數中還有一個參數是fairness,能夠參看前面記錄的fairness的做用;
該類中還有兩個方法
  • acquireUninterruptibly(): 因爲acquire()方法在計數器爲0時,線程將被阻塞,在這期間線程可能被interrupt,因此會拋出InterrupteException異常,可是使用該方法能夠避免拋出異常;
  • tryAcquire():該方法返回boolean類型,能夠控制不讓線程進入阻塞狀態;
相關文章
相關標籤/搜索