JAVA多線程之——Semaphore

Semaphore

一個計數信號量。從概念上講,信號量維護了一個許可集。如有必要,在許可可用前會阻塞每一個 acquire(),然後再獲取該許可。每個 release() 添加一個許可,從而可能釋放一個正在阻塞的獲取者。但是,不使用實際的許可對象,Semaphore 只對可用許可的號碼進行計數,並採取相應的行動。
其實Semaphore就是維護了一個共享鎖,通過state來決定同時可以多少個線程獲取共享鎖。

public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

acquire就是獲取共享鎖。
acquireSharedInterruptibly

public final void acquireSharedInterruptibly(int arg)
         throws InterruptedException {
     if (Thread.interrupted())  //線程被中斷拋出中斷異常
         throw new InterruptedException();
     if (tryAcquireShared(arg) < 0)   //嘗試獲取鎖
         doAcquireSharedInterruptibly(arg);//調用方法doAcquireSharedInterruptibly
 }

doAcquireSharedInterruptibly

private void doAcquireSharedInterruptibly(int arg)
       throws InterruptedException {
       final Node node = addWaiter(Node.SHARED);//// 創建”當前線程“的Node節點,且Node中記錄的鎖是”共享鎖“類型;並將該節點添加到CLH隊列末尾。
       boolean failed = true;
       try {
           for (;;) {
               final Node p = node.predecessor(); //獲取前一個節點
               if (p == head) {  //如果上一個節點是頭節點
                   int r = tryAcquireShared(arg);//嘗試獲取鎖(上一個是頭節點,那麼可能此時上一個節點已經成功獲取了鎖,所以嘗試獲取一下)
                   if (r >= 0) {   //獲取成功
                       setHeadAndPropagate(node, r); //設置頭節點
                       p.next = null; // help GC
                       failed = false;
                       return;
                   }
               }
               // 當前線程一直等待,直到獲取到共享鎖。
            // 如果線程在等待過程中被中斷過,則再次中斷該線程(還原之前的中斷狀態)。
               if (shouldParkAfterFailedAcquire(p, node) &&
                   parkAndCheckInterrupt())
                   throw new InterruptedException();
           }
       } finally {
           if (failed)
               cancelAcquire(node);
       }
   }

因此Semaphore就是同時可以讓多個線程去獲取共享鎖,如果線程達到指定閥值,那麼就等待,一直等到其它線程調用了release釋放。
所以,Semaphore經常用來控制公共資源的併發訪問。比如對某一個文件限制只能同時10個線程進行讀取。

public class SemaphoreTest {

    static Semaphore semaphore = new Semaphore(10);
    public static void main(String[] args) {
        for(int i = 0 ; i < 100; i++) {
           new ReadFile().start();
        }

    }

    static class ReadFile extends Thread{

        @Override
        public void run() {
            try {
                semaphore.acquire();
                System.out.println(Thread.currentThread().getName() + "開始讀取文件");
                Thread.currentThread().sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally{
                // System.out.println(Thread.currentThread().getName() + "釋放了一個許可");
                 //semaphore.release();
            }
        }

    }

}

某次運行結果:

Thread-0開始讀取文件
Thread-1開始讀取文件
Thread-4開始讀取文件
Thread-2開始讀取文件
Thread-3開始讀取文件
Thread-5開始讀取文件
Thread-7開始讀取文件
Thread-9開始讀取文件
Thread-6開始讀取文件
Thread-8開始讀取文件

結果是隻有是個線程開始讀取,其它線程都在等待,沒有打印出來。如果把上面的註釋代碼去掉。那麼有釋放鎖,就會讓其它線程可以讀取。

發佈了61 篇原創文章 · 獲贊 23 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章