java多線程之Semaphore信號量詳解

   信號量在操作系統中一般用來管理數量有限的資源.每類資源有一個對應的信號量.信號量的值表示資源的可用數量.在使用資源時,要先從該信號量上獲取一個使用許可.成功獲取許可之後,資源可用數量減1.在持有許可期,使用者可以對獲取資源進行操作.完成對資源的使用之後,需要在信號量上釋放一個許可,資源可用數加1,允許其他使用者獲取資源.當資源可用數爲0的時候,需要獲取資源的線程以阻塞的方式來等待資源變爲可用,或者過段時間之後再檢查資源是否變爲可用.

   在java中有相應的Semaphore實現類,在創建Semaphore類的對象時指定資源的可用數,通過acquire方法以阻塞式的方式獲取許可,而tryAcquire方法以非阻塞式的方式來獲取許可.當需要釋放許可時,使用release方法.Semaphore類也支持同時獲取和釋放多個資源的許可.通過acquire方法獲取許可的過程是可以被中斷的.如果不希望被中斷,那麼可以使用acquireUninterruptibly方法.Semaphore也支持在分配許可時使用公平模式,通過把構造方法的第二個參數設置爲true來使用該模式.在公平模式下,當資源可用時,等待線程按照調用acquire方法申請資源的順序依次獲取許可.在進行資源管理時,一般使用公平模式,以避免造成線程飢渴問題.需要注意的是獲取資源時,通過synchronized關鍵詞或鎖聲明同步.這是因爲Semaphore類只是一個資源數量的抽象表示,並不負責管理資源對象本身,可能有多個線程同時獲取到資源使用許可,因此需要使用同步機制避免數據競爭.

使用信號量管理資源的示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.ReentrantLock;
 
/**
 * 信號量使用詳解,使用信號量來管理有限的資源
 * User: ketqi
 * Date: 2013-01-27 12:29
 */
public class SemaphoreDemo {
    /** 可重入鎖,對資源列表進行同步 */
    private final ReentrantLock lock = new ReentrantLock();
    /** 信號量 */
    private final Semaphore semaphore;
    /** 可使用的資源列表 */
    private final LinkedList<Object> resourceList = new LinkedList<Object>();
 
    public SemaphoreDemo(Collection<Object> resourceList) {
        this.resourceList.addAll(resourceList);
        this.semaphore = new Semaphore(resourceList.size(), true);
    }
 
    /**
     * 獲取資源
     *
     * @return 可用的資源
     * @throws InterruptedException
     */
    public Object acquire() throws InterruptedException {
        semaphore.acquire();
 
        lock.lock();
        try {
            return resourceList.pollFirst();
        finally {
            lock.unlock();
        }
    }
 
    /**
     * 釋放或者歸還資源
     *
     * @param resource 待釋放或歸還的資源
     */
    public void release(Object resource) {
        lock.lock();
        try {
            resourceList.addLast(resource);
        finally {
            lock.unlock();
        }
 
        semaphore.release();
    }
 
    public static void main(String[] args) {
        //準備2個可用資源
        List<Object> resourceList = new ArrayList<>();
        resourceList.add("Resource1");
        resourceList.add("Resource2");
 
        //準備工作任務
        final SemaphoreDemo demo = new SemaphoreDemo(resourceList);
        Runnable worker = new Runnable() {
            @Override
            public void run() {
                Object resource = null;
                try {
                    //獲取資源
                    resource = demo.acquire();
                    System.out.println(Thread.currentThread().getName() + "\twork   on\t" + resource);
                    //用resource做工作
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + "\tfinish on\t" + resource);
                catch (InterruptedException e) {
                    e.printStackTrace();
                finally {
                    //歸還資源
                    if (resource != null) {
                        demo.release(resource);
                    }
                }
            }
        };
 
        //啓動9個任務
        ExecutorService service = Executors.newCachedThreadPool();
        for (int i = 0; i < 9; i++) {
            service.submit(worker);
        }
        service.shutdown();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章