一、概念
Semaphore也是一個線程同步的輔助類,可以維護當(dāng)前訪問自身的線程個數(shù),并提供了同步機制。使用Semaphore可以控制同時訪問資源的線程個數(shù),例如,實現(xiàn)一個文件允許的并發(fā)訪問數(shù)。
二、Semaphore的主要方法:
void acquire():從此信號量獲取一個許可,在提供一個許可前一直將線程阻塞,否則線程被中斷。
void release():釋放一個許可,將其返回給信號量。
int availablePermits():返回此信號量中當(dāng)前可用的許可數(shù)。
boolean hasQueuedThreads():查詢是否有線程正在等待獲取。
三、為什么要用Semaphore
Semaphore有兩個目的,第一個是多個共享資源互斥使用,第二個是并發(fā)線程數(shù)的控制,具體例子如下:

WX20201116-162836.png
6個線程同時執(zhí)行一個工作,只允許最多3個同時執(zhí)行,由上圖可以看出,release() 釋放資源的順序不一定遵循進入的順序。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SemaphoreTest {
public static void main(String[] args) {
testSemaphore();
}
public static void testSemaphore() {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(200);
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
//請求獲得許可,如果有可獲得的許可則繼續(xù)往下執(zhí)行,許可數(shù)減1。否則進入阻塞狀態(tài)
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程" + Thread.currentThread().getName() +
"進入,當(dāng)前已有" + (3 - semaphore.availablePermits()) + "個并發(fā)");
try {
Thread.sleep((long) (Math.random() * 10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程" + Thread.currentThread().getName() +
"即將離開");
semaphore.release();//釋放許可,許可數(shù)加1
//下面代碼有時候執(zhí)行不準確,因為其沒有和上面的代碼合成原子單元
System.out.println("線程" + Thread.currentThread().getName() +
"已離開,當(dāng)前已有" + (3 - semaphore.availablePermits()) + "個并發(fā)");
}
};
executorService.execute(runnable);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
三、 同ReentrantLock比較,Semaphore內(nèi)部也是依靠一個繼承自AbstractQueuedSynchronizer的Sync抽象類型的類成員變量sync來實現(xiàn)主要功能的, 單個信號量的Semaphore對象可以實現(xiàn)互斥鎖的功能,并且可以是由一個線程獲得了“鎖”,再由另一個線程釋放“鎖”,這可應(yīng)用于死鎖恢復(fù)的一些場合。
import java.sql.BatchUpdateException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest {
public static void main(String[] args) {
final Business business = new Business();
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new Runnable() {
@Override
public void run() {
business.service();
}
});
}
private static class Business {
private int count;
Lock lock = new ReentrantLock();
Semaphore sp = new Semaphore(1);
public void service() {
//lock.lock();
try {
sp.acquire(); //當(dāng)前線程使用count變量的時候?qū)⑵滏i住,不允許其他線程訪問
} catch (InterruptedException e1) {
e1.printStackTrace();
}
try {
count++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(count);
} catch (RuntimeException e) {
e.printStackTrace();
} finally {
//lock.unlock();
sp.release(); //釋放鎖
}
}
}
}