信號量用于線程同步,互斥量用戶保護資源的互斥訪問。
信號量與互斥量的區別
- 互斥量用于線程的互斥,信號線用于線程的同步。
- 互斥量值只能為0/1,信號量值可以為非負整數。信號量可以實現多個同類資源的多線程互斥和同步。
- 互斥量的加鎖和解鎖必須由同一線程分別對應使用,信號量可以由一個線程釋放,另一個線程得到。
信號量Semaphore
信號量是在多線程環境中,線程間傳遞信號的一種方式。
簡單的Semaphore實現
public class Semaphore {
private boolean signal = false; //使用signal可以避免信號丟失
public synchronized void take() {
this.signal = true;
this.notify();
}
public synchronized void release() throws InterruptedException{
while(!this.signal) //使用while避免假喚醒
wait();
this.signal = false;
}
}
使用場景
Semaphore semaphore = new Semaphore();
SendingThread sender = new SendingThread(semaphore);
ReceivingThread receiver = new ReceivingThread(semaphore);
receiver.start();
sender.start();
public class SendingThread {
Semaphore semaphore = null;
public SendingThread(Semaphore semaphore){
this.semaphore = semaphore;
}
public void run(){
while(true){
//do something, then signal
this.semaphore.take();
}
}
}
public class RecevingThread {
Semaphore semaphore = null;
public ReceivingThread(Semaphore semaphore){
this.semaphore = semaphore;
}
public void run(){
while(true){
this.semaphore.release();
//receive signal, then do something...
}
}
}
可計數的Semaphore
上面提到的Semaphore的簡單實現并沒有計算通過調用take方法所產生信號的數量。可以把它改造成具有計數功能的Semaphore。
public class CountingSemaphore {
private int signals = 0;
public synchronized void take() {
this.signals++;
this.notify();
}
public synchronized void release() throws InterruptedException{
while(this.signals == 0)
wait();
this.signals--;
}
}
有上限的Semaphore
可以將上面的CountingSemaphore改造成一個信號數量有上限的BoundedSemaphore
public class BoundedSemaphore {
private int signals = 0;
private int bound = 0;
public BoundedSemaphore(int upperBound){
this.bound = upperBound;
}
public synchronized void take() throws InterruptedException{
while(this.signals == bound)
wait();
this.signals++;
this.notify();
}
public synchronized void release() throws InterruptedException{
while(this.signals == 0)
wait();
this.signals--;
this.notify();
}
}
在BoundedSemaphore中,當已經產生的信號數量達到了上限,take方法將阻塞新的信號產生請求,直到某個線程調用release方法后,被阻塞于take方法的線程才能傳遞自己的信號。
Java內置的Semaphore
java.util.concurrent包中有Semaphore的實現,可以設置參數,控制同時訪問的個數。
下面的Demo中申明了一個只有5個許可的Semaphore,而有20個線程要訪問這個資源,通過acquire()和release()獲取和釋放訪問許可。
final Semaphore semp = new Semaphore(5);
ExecutorService exec = Executors.newCachedThreadPool();
for (int index = 0; index < 20; index++) {
final int NO = index;
Runnable run = new Runnable() {
public void run() {
try {
// 獲取許可
semp.acquire();
System.out.println("Accessing: " + NO);
Thread.sleep((long) (Math.random() * 10000));
// 訪問完后,釋放
semp.release();
System.out.println("-----------------" + semp.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
exec.execute(run);
}
exec.shutdown();
互斥量Mutex
互斥量:提供對資源的獨占訪問,只能為0/1,如果某一個資源同時只能允許一個訪問者對其訪問,可以使用互斥量控制線程對其訪問。
互斥量實現:
public class Mutex {
private boolean isLocked = false;
public synchronized void lock() {
while(this.isLocked) //使用while可以避免線程 假喚醒
wait();
this.isLocked= true;
}
}
public synchronized void unlock() throws InterruptedException{
this.isLocked= false;
this.notify();
}
}
在Mutex中,我們添加了一個signal用于保存信號。
將互斥量當作鎖來使用:
Mutex mutex = new Mutex();
mutex.lock();
...
//臨界區
mutex.unlock();
互斥量的加鎖和解鎖必須由同一個線程分別對應使用。