天天看點

15_張孝祥_多線程_Semaphere同步工具

java Semaphere可類比作業系統信号量,硬體資源如IO、記憶體、磁盤等都是有固定量的,多個程式需要競争這些資源,沒有資源就需要被挂起。

類和方法摘要

構造函數:

public Semaphore(int permits):建立具有給定的許可數和非公平的公平設定的 Semaphore。

-參數 :

permits - 初始的可用許可數目。此值可能為負數,在這種情況下,必須在授予任何擷取前進行釋放。

public Semaphore(int permits, boolean fair):建立具有給定的許可數和給定的公平設定的 Semaphore。

-參數:

permits - 初始的可用許可數目。此值可能為負數,在這種情況下,必須在授予任何擷取前進行釋放。

fair - 如果此信号量保證在争用時按先進先出的順序授予許可,則為 true;否則為 false。

常用方法:

void acquire():從此信号量擷取一個許可,在提供一個許可前一直将線程阻塞,否則線程被中斷。

void release():釋放一個許可,将其傳回給信号量。

int availablePermits():傳回此信号量中目前可用的許可數。

boolean hasQueuedThreads():查詢是否有線程正在等待擷取。

使用技巧

将信号量初始化為 1,使得它在使用時最多隻有一個可用的許可,進而可用作一個互相排斥的鎖。這通常也稱為二進制信号量,因為它隻能有兩種狀态:一個可用的許可,或零個可用的許可。按此方式使用時,二進制信号量具有某種屬性(與很多 Lock 實作不同),即可以由線程釋放“鎖”,而不是由所有者(因為信号量沒有所有權的概念)。在某些專門的上下文(如死鎖恢複)中這會很有用。

對比線程池

線程池控制的是線程數量,而信号量控制的是并發數量,雖然說看起來一樣,但兩者還是有差別的。

信号量類似于鎖機制,信号量的調用,當達到數量後,線程還是存在的,隻是被挂起了而已。而線程池,同時執行的線程數量是固定的,超過了數量的隻能等待。

代碼示例

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class SemaphoreTest {
    public static void main(String[] args) {
        ExecutorService service = Executors.newCachedThreadPool();
        final  Semaphore sp = new Semaphore();
        for(int i=;i<;i++){
            Runnable runnable = new Runnable(){
                    public void run(){
                    try {
                        sp.acquire();
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                    System.out.println("線程" + Thread.currentThread().getName() + 
                            "進入,目前已有" + (-sp.availablePermits()) + "個并發");
                    try {
                        Thread.sleep((long)(Math.random()*));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("線程" + Thread.currentThread().getName() + 
                            "即将離開");                    
                    sp.release();
                    //下面代碼有時候執行不準确,因為其沒有和上面的代碼合成原子單元
                    System.out.println("線程" + Thread.currentThread().getName() + 
                            "已離開,目前已有" + (-sp.availablePermits()) + "個并發");                    
                }
            };
            service.execute(runnable);          
        }
    }

}
           

輸出:

線程pool--thread-進入,目前已有個并發
線程pool--thread-進入,目前已有個并發
線程pool--thread-進入,目前已有個并發
線程pool--thread-即将離開
線程pool--thread-已離開,目前已有個并發
線程pool--thread-進入,目前已有個并發
線程pool--thread-即将離開
線程pool--thread-已離開,目前已有個并發
線程pool--thread-進入,目前已有個并發
線程pool--thread-即将離開
線程pool--thread-已離開,目前已有個并發
線程pool--thread-進入,目前已有個并發
線程pool--thread-即将離開
線程pool--thread-已離開,目前已有個并發
線程pool--thread-進入,目前已有個并發
線程pool--thread-即将離開
線程pool--thread-已離開,目前已有個并發
線程pool--thread-進入,目前已有個并發
線程pool--thread-即将離開
線程pool--thread-已離開,目前已有個并發
線程pool--thread-進入,目前已有個并發
線程pool--thread-即将離開
線程pool--thread-已離開,目前已有個并發
線程pool--thread-進入,目前已有個并發
線程pool--thread-即将離開
線程pool--thread-已離開,目前已有個并發
線程pool--thread-即将離開
線程pool--thread-已離開,目前已有個并發
線程pool--thread-即将離開
線程pool--thread-已離開,目前已有個并發
           

可以同時有3個線程執行sp.acquire()到sp.release()之間的代碼。