併發編程:信號量入門---轉

  轉--------http://www.java3z.com/cwbwebhome/article/article1/1323.html

一、信號量

     信號量通過一個計數器控制對共享資源的訪問。如果計數器大於0,則訪問被允許,如果爲0,則訪問被禁止。 計數器計算的結果是允許訪問共享資源的通行證。因此,爲了訪問共享資源,線程必須從信號量得到通行證, 如果該信號量的計數大於0,則此線程獲得一個通行證,這將導致信號量的計數遞減,否則,此線程將阻塞直到獲得一個通行證爲止。當此線程不再需要訪問共享資源時,它釋放該通行證,這導致信號量的計數遞增,如果
另一個線程等待通行證,則那個線程將在那時獲得通行證。java的Semaphore類實現了這種機制。


Semaphore的兩個構造函數
Semaphore(int num)
Semaphore(int num,boolean how)

 

  num指定初始通行證計數。因此,num指定了每次能夠訪問共享資源的線程數,如果num是1,則只有一個線程每次都能夠訪問共享資源。默認情況下,以未定義次序授予等待線程的通行證,如果將how設爲true, 可確保以請求訪問的次序授予等待線程的通行證。

要獲得通行證,調用acquire()方法

void acquire() throws InterruptedException
void acquire(int num) throws InterruptedException

第一種形式獲得一個通行證,第二種形式獲得num個通行證。如果該通行證無法在調用時授予,則調用線程暫停,直到獲得通行證。

要釋放通行證,調用repease方法,
void release()
void release(int num)

分別釋放一個或num個通行證。

爲了用一個信號量控制對資源的訪問,使用該資源的每個線程在訪問該資源之前,首先要調用acquire(), 當一個線程結束對資源的使用後,它必須調用release()。

二、下面的例子示範如何使用信號量:

import java.util.concurrent.*; 

class SemDemo {

public static void main(String args[]) {
Semaphore sem = new Semaphore(1);

new IncThread(sem, "A");
new DecThread(sem, "B");

}
}

// A shared resource.
class Shared {
static int count = 0;
}

// A thread of execution that increments count.
class IncThread implements Runnable {
String name;
Semaphore sem;

IncThread(Semaphore s, String n) {
sem = s;
name = n;
new Thread(this).start();
}

public void run() {

System.out.println("Starting " + name);

try {
// First, get a permit.
System.out.println(name + " is waiting for a permit.");
sem.acquire();
System.out.println(name + " gets a permit.");

// Now, access shared resource.
for(int i=0; i < 5; i++) {
Shared.count++;
System.out.println(name + ": " + Shared.count);

// Now, allow a context switch -- if possible.
Thread.sleep(10);
}
} catch (InterruptedException exc) {
System.out.println(exc);
}

// Release the permit.
System.out.println(name + " releases the permit.");
sem.release();
}
}

// A thread of execution that deccrements count.
class DecThread implements Runnable {
String name;
Semaphore sem;

DecThread(Semaphore s, String n) {
sem = s;
name = n;
new Thread(this).start();
}

public void run() {

System.out.println("Starting " + name);

try {
// First, get a permit.
System.out.println(name + " is waiting for a permit.");
sem.acquire();
System.out.println(name + " gets a permit.");

// Now, access shared resource.
for(int i=0; i < 5; i++) {
Shared.count--;
System.out.println(name + ": " + Shared.count);

// Now, allow a context switch -- if possible.
Thread.sleep(10);
}
} catch (InterruptedException exc) {
System.out.println(exc);
}

// Release the permit.
System.out.println(name + " releases the permit.");
sem.release();
}
}
程序的輸出:
C:/java>java SemDemo
Starting A
A is waiting for a permit.
A gets a permit.
A: 1
Starting B
B is waiting for a permit.
A: 2
A: 3
A: 4
A: 5
A releases the permit.
B gets a permit.
B: 4
B: 3
B: 2
B: 1
B: 0
B releases the permit.

   這個程序使用信號量控制對Shared類中靜態變量count的訪問,爲了防止這兩個 線程同時訪問Shared.count,僅當線程從控制信號量獲得通行證之後才允許訪問,訪問結束後,該通行證被釋放。通過這種方式,保證一次只有一個線 程訪問Shared.count.

      在run()中,sleep()的調用導致調用線程每次訪問Shared.count之後都暫停,這通常會使第二個線程運行, 但在這裏,由於信號量的原因,第二個線程必須等待第一個線程釋放通行證,而這要等到第一個線程對共享資源的所有訪問都結束之後才能發生。

三、使用兩個信號量同步生產者和消費者線程。

import java.util.concurrent.Semaphore; 

class Q { //倉庫
int n; //倉庫中的產品

// Start with consumer semaphore unavailable.
static Semaphore semCon = new Semaphore(0);
static Semaphore semProd = new Semaphore(1);

void get() {
try {
semCon.acquire();
} catch(InterruptedException e) {
System.out.println("InterruptedException caught");
}

System.out.println("Got: " + n);
semProd.release();
}

void put(int n) {
try {
semProd.acquire();
} catch(InterruptedException e) {
System.out.println("InterruptedException caught");
}

this.n = n;
System.out.println("Put: " + n);
semCon.release();
}
}

class Producer implements Runnable { //生產者
Q q;

Producer(Q q) {
this.q = q;
new Thread(this, "Producer").start();
}

public void run() {
for(int i=0; i < 20; i++) q.put(i);
}
}

class Consumer implements Runnable { //消費者
Q q;

Consumer(Q q) {
this.q = q;
new Thread(this, "Consumer").start();
}

public void run() {
for(int i=0; i < 20; i++) q.get();
}
}

class ProdCon { //測試類
public static void main(String args[]) {
Q q = new Q();
new Consumer(q);
new Producer(q);
}
}
程序的部分輸出:

C:/java>java ProdCon
Put: 0
Got: 0
Put: 1
Got: 1
Put: 2
Got: 2
Put: 3
Got: 3
Put: 4
Got: 4
Put: 5
Got: 5
Put: 6
Got: 6
Put: 7
Got: 7
Put: 8
Got: 8
Put: 9
Got: 9
Put: 10
Got: 10

    可以看到,put()調用和get()調用是同步的。每次調用一個put()都會隨後調用一個get(),不會有失配的值。如果沒有信號量,put()的多次調用可能會沒有與其匹配的get()調用。從而導致出現失配的值,可刪除信號量代碼驗證。

   put()和get()調用通過兩個信號量來處理:semProd和semCon。在put()生成一個值之前,它必須從semProd獲得一個通 行證。它設置該值後釋放semCon。在get()使用一個值之前,它必須從semCon獲得一個通過證。它使用該值後,必須釋放semPord。這種“ 給與受”的機制確保put()的每次調用必須跟隨一個get()調用。

   semCon在初始化的時候無法獲得通行證,從而保證了put()首先運行。設置初始同步狀態的能力是信號量的強大表現之一。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章