Thread & Semaphore
Thread & Semaphore
在 Java 中,Semaphore 可以用來控制多個執行緒對同一物件的存取。Semaphore 會維護一個數字,稱為 permits,代表可用的許可數量。當一個執行緒想要存取該物件時,它會先呼叫 acquire() 方法來獲取一個許可。如果 permits 大於 0,執行緒會成功獲取一個許可,並且 permits 的數量會減少。如果 permits 等於 0,執行緒會等待直到有其他執行緒釋放許可。
當一個執行緒完成對該物件的存取後,它會呼叫 release() 方法來釋放許可。這會導致 permits 的數量增加,並且可能會喚醒正在等待許可的其他執行緒。
Semaphore 的使用可以簡化程式碼,讓它更易讀和理解。相較於使用 synchronized、wait 和 notifyAll 來實現同樣的功能,Semaphore 提供了一種更直觀的方式來控制多個執行緒對同一物件的存取。
public class SharedObject {
private Semaphore semaphore = new Semaphore(1);
public void doSomething() {
try {
semaphore.acquire();
// 存取共享物件的程式碼
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
}
在上面的範例中,我們創建了一個 Semaphore 物件,並且初始化 permits 為 1,表示只有一個許可。在 doSomething() 方法中,我們首先呼叫 acquire() 方法來獲取許可,然後進行對共享物件的存取操作。最後,我們呼叫 release() 方法來釋放許可。
這樣,當多個執行緒同時存取 SharedObject 物件時,只有一個執行緒可以成功獲取許可,其他執行緒則需要等待。這樣可以確保同一時間只有一個執行緒在存取該物件,避免了錯亂的情況。
參考:How to use a Semaphore in Java with code examples https://davidvlijmincx.com/posts/how-to-use-java-semaphore/
範例
如何用不同執行緒,呼叫不同方法,讓其每次輸出能依序列印出 1、2、3。
class Foo {
public Foo() {
}
public void first() throws InterruptedException {
System.out.println("1");
}
public void second() throws InterruptedException {
System.out.println("2");
}
public void third() throws InterruptedException {
System.out.println("3");
}
public static void main(String[] args) throws InterruptedException {
Foo foo = new Foo();
Runnable printFirst = new Runnable() {
@Override
public void run() {
try {
foo.first();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
Runnable printSecond = new Runnable() {
@Override
public void run() {
try {
foo.second();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
Runnable printThird = new Runnable() {
@Override
public void run() {
try {
foo.third();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
Thread one = new Thread(printFirst);
Thread two = new Thread(printSecond);
Thread three = new Thread(printThird);
three.start();
two.start();
one.start();
}
}
方法一、用 synchronized、wait、notifyAll 方法實現
public class Foo {
boolean a;
boolean b;
boolean c;
public synchronized void first() throws InterruptedException {
System.out.println("1");
a = true;
notifyAll();
}
public synchronized void second() throws InterruptedException {
while(!a) {
wait();
}
System.out.println("2");
b = true;
notifyAll();
}
public synchronized void third() throws InterruptedException {
while(!b) {
wait();
}
System.out.println("3");
c = true;
notifyAll();
}
public Foo() {
a = false;
b = false;
c = false;
}
// main 方法省略
}
方法二、用 Semaphore 方法實現
public class Foo2 {
private Semaphore a = new Semaphore(1);
private Semaphore b = new Semaphore(0);
private Semaphore c = new Semaphore(0);
public Foo2() {
}
public void first() throws InterruptedException {
a.acquire(1);
System.out.println("1");
b.release(1);
}
public void second() throws InterruptedException {
b.acquire(1);
System.out.println("2");
c.release(1);
}
public void third() throws InterruptedException {
c.acquire(1);
System.out.println("3");
}
// main 方法省略
}