Semaphore
Semaphore即信號、信號系統。此類的主要作用就是限制線程併發的數量,如果不限制線程併發的數量,則CPU的資源很快就被耗盡,每個線程執行的任務是相當緩慢,因爲CPU要把時間片分配給不同的線程對象,而且上下文切換也要耗時,最終造成系統運行效率大幅降低,所以限制併發線程的數量還是非常有必要的。
Semaphore的同步性
public class Service {
private Semaphore semaphore = new Semaphore(1);
public void testMethod() {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "begin:" + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "end:" + System.currentTimeMillis());
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
ThreadC c = new ThreadC(service);
c.setName("C");
c.start();
}
}
class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.testMethod();
}
}
class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.testMethod();
}
}
class ThreadC extends Thread {
private Service service;
public ThreadC(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.testMethod();
}
}
類Semaphore的構造函數參數permits是許可的意思,代表同一時間內,最多允許多少個線程同時執行acquire()和release()之間的代碼。無參方法acquire(的作用是使用1個許可,是減法操作。
其實還可以傳人>1的許可,代表同-時間內,最多允許有x個線程可以執行acquire()和release()之間的代碼。
acquire(int permits):
每調用一次方法,就使用permits個許可。
release(int permits):
每調用一次方法,就增加permits個許可,可以動態增加許可的個數:
Semaphore semaphore = new Semaphore(5);
semaphore.acquire();
semaphore.acquire();
semaphore.acquire();
semaphore.acquire();
semaphore.acquire();
System.out.println(semaphore.availablePermits());
semaphore.release();
semaphore.release();
semaphore.release();
semaphore.release();
semaphore.release();
semaphore.release();
System.out.println(semaphore.availablePermits());
semaphore.release(4);
System.out.println(semaphore.availablePermits());
acquireUninterruptibly():
使等待進入acquire()方法的線程,不允許被中斷。
public class Service2 {
private Semaphore semaphore = new Semaphore(1);
public void test() {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "begin:" + System.currentTimeMillis());
for (int i = 0; i < Integer.MAX_VALUE / 100; i++) {
String str = new String();
Math.random();
}
System.out.println(Thread.currentThread().getName() + "end:" + System.currentTimeMillis());
semaphore.release();
} catch (InterruptedException e) {
System.out.println("線程" + Thread.currentThread().getName() + "進入了Catch");
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Service2 service2 = new Service2();
ThreadA a = new ThreadA(service2);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service2);
b.setName("B");
b.start();
b.interrupt();
System.out.println("main");
}
}
class ThreadA extends Thread {
private Service2 service;
public ThreadA(Service2 service) {
super();
this.service = service;
}
@Override
public void run() {
service.test();
}
}
class ThreadB extends Thread {
private Service2 service;
public ThreadB(Service2 service) {
super();
this.service = service;
}
@Override
public void run() {
service.test();
}
}
線程B成功被中斷。
修改acquire方法:
再次運行效果如下:
acquireUninterruptibly()方法還有重載的寫法acquireUninterruptibly(int permits)
,此方法的作用是在等待許可的情況下不允許中斷,如果成功獲得鎖,則取得指定的permits許可個數。
availablePermits()和drainPermits()方法:
availablePermits()
返回此Semaphore對象中當前可用的許可數,此方法通常用於調試,因爲許可的數量有可能實時在改變,並不是固定的數量。
drainPermits()
可獲取並返回立即可用的所有許可個數,並且將可用許可置0。
Semaphore semaphore = new Semaphore(10);
semaphore.acquire();
System.out.println(semaphore.drainPermits() + " " + semaphore.availablePermits());
System.out.println(semaphore.drainPermits() + " " + semaphore.availablePermits());
System.out.println(semaphore.drainPermits() + " " + semaphore.availablePermits());
semaphore.release();
getQueueLength()和hasQueuedThreads():
方法getQueueLength()
的作用是取得等待許可的線程個數。
方法hasQueuedThreads()
的作用是判斷有沒有線程在等待這個許可。
這兩個方法通常都是在判斷當前有沒有等待許可的線程信息時使用。
public class MyService {
private Semaphore semaphore = new Semaphore(1);
public void test() {
try {
semaphore.acquire();
Thread.sleep(1000);
System.out.println("還有大約" + semaphore.getQueueLength() + "個線程在等待");
System.out.println("是否有線程正在等待信號量?" + semaphore.hasQueuedThreads());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
public static void main(String[] args) {
MyService myService = new MyService();
MyThread firstThread = new MyThread(myService);
firstThread.start();
MyThread[] arr = new MyThread[4];
for (int i = 0; i < 4; i++) {
arr[i] = new MyThread(myService);
arr[i].start();
}
}
}
公平與非公平信號量:
有些時候,獲得許可的順序與線程啓動的順序有關,這時信號量就要分爲公平與非公平的。所謂的公平信號量是獲得鎖的順序與線程啓動的順序有關,但不代表100%地獲得信號量,僅僅是在概率上能得到保證。而非公平信號量就是無關的了。
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
構造中傳入true,即爲公平信號量;反之爲非公平。公平信號量運行的效果是線程啓動的順序與調用semaphore.acquire()
的順序有關,也就是先啓動的線程優先獲得許可。
tryAcquire():
無參方法tryAcquire()的作用是嘗試地獲得1個許可,如果獲取不到則返回false,此方法通常與if語句結合使用,其具有無阻塞的特點。無阻塞的特點可以使線程不至於在同步處一直持續等待的狀態,如果if語句判斷不成立則線程會繼續走else語句,程序會繼續向下運行。
tryAcquire(int permits):
有參方法tryAcquire(int permits)的作用是嘗試地獲得permits個許可,如果獲取不到則返回false。
tryAcquire(long timeout, TimeUnit unit):
有參方法tryAcquire(int long timeout,TimeUnit unit)的作用是在指定的時間內嘗試地獲得1個許可,如果獲取不到則返回false。
tryAcquire(int permits, long timeout, TimeUnit unit):
有參方法tryAcquire(int permits,long timeout,TimeUnit unit)的作用是在指定的時間內嘗試地獲得permits個許可,如果獲取不到則返回false。
使用Semaphore實現多生產者/多消費者模式
public class RepastService {
volatile private Semaphore setSemaphore = new Semaphore(10);//10個廚師
volatile private Semaphore getSemaphore = new Semaphore(20);//食客
volatile private ReentrantLock lock = new ReentrantLock();
volatile private Condition setCondition = lock.newCondition();
volatile private Condition getCondition = lock.newCondition();
//只有3個盤子放菜品
volatile private Object[] producePosition = new Object[4];
private boolean isEmpty() {
boolean isEmty = true;
for (int i = 0; i < producePosition.length; i++) {
if (producePosition[i] != null) {
isEmty = false;
break;
}
}
if (isEmty == true) {
return true;
} else {
return false;
}
}
private boolean isFull() {
boolean isFull = true;
for (int i = 0; i < producePosition.length; i++) {
if (producePosition[i] == null) {
isFull = false;
break;
}
}
return isFull;
}
public void set() {
try {
setSemaphore.acquire();//允許同時最多有10個廚師炒菜
lock.lock();
while (isFull()) {
setCondition.await();
}
for (int i = 0; i < producePosition.length; i++) {
if (producePosition[i] == null) {
producePosition[i] = "菜";
System.out.println(Thread.currentThread().getName() + "生產了" + producePosition[i]);
break;
}
}
getCondition.signalAll();
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
setSemaphore.release();
}
}
public void get() {
try {
getSemaphore.acquire();//允許同時最多20個食客
lock.lock();
while (isEmpty()) {
getCondition.await();
}
for (int i = 0; i < producePosition.length; i++) {
if (producePosition[i] != null) {
System.out.println(Thread.currentThread().getName() + "吃了" + producePosition[i]);
producePosition[i] = null;
break;
}
}
setCondition.signalAll();
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
getSemaphore.release();
}
}
public static void main(String[] args) throws InterruptedException {
RepastService service = new RepastService();
ThreadP[] arrP = new ThreadP[60];
ThreadC[] arrC = new ThreadC[60];
for (int i = 0; i < 60; i++) {
arrP[i] = new ThreadP(service);
arrC[i] = new ThreadC(service);
}
Thread.sleep(2000);
for (int i = 0; i < 60; i++) {
arrP[i].start();
arrC[i].start();
}
}
}
//廚師
class ThreadP extends Thread {
private RepastService service;
public ThreadP(RepastService service) {
this.service = service;
}
@Override
public void run() {
service.set();
}
}
//食客
class ThreadC extends Thread {
private RepastService service;
public ThreadC(RepastService service) {
this.service = service;
}
@Override
public void run() {
service.get();
}
}