Semaphore是一個計數信號量,採用的是共享鎖的方式來控制。
主要對下面2個方法進行分析:
acquire(int)來獲取信號量,直到只有一個可以用或者出現中斷。release(int)用來釋放信號量,將信號量數量返回給Semaphore
public void acquire(int permits) throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireSharedInterruptibly(permits);
}
AQS:acquireSharedInterruptibly
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0) //小於0表示沒有獲得共享鎖
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();//獲取去中的信號量數
int remaining = available - acquires;//剩餘信號量數
//1.信號量數大於0,獲取共享鎖,並設置執行compareAndSetState(available, remaining),返回剩餘信號量數
//2.信號量數小於等於0,直接返回負數
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
/**
* The synchronization state.
*/
private volatile int state;
/**
* Returns the current value of synchronization state.
* This operation has memory semantics of a <tt>volatile</tt> read.
* @return current state value
*/
protected final int getState() {
return state;
}
//沒有獲得共享鎖
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
//加入等待隊列
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
//前面的節點等於頭結點
if (p == head) {
//嘗試獲取共享鎖
int r = tryAcquireShared(arg);
if (r >= 0) {
//將當前節點設置爲head
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
// 當前線程一直等待,直到獲取到共享鎖。
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
釋放信號量release(int)的源碼
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();//獲取當前的信號量數
int next = current + releases;//將釋放的信號量還給Semaphore
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))//設置信號量
return true;
}
}
private void doReleaseShared() {
for (;;) {
// 獲取CLH隊列的頭節點
Node h = head;
// 如果頭節點不爲null,並且頭節點不等於tail節點。
if (h != null && h != tail) {
// 獲取頭節點對應的線程的狀態
int ws = h.waitStatus;
// 如果頭節點對應的線程是SIGNAL狀態,則意味着“頭節點的下一個節點所對應的線程”需要被unpark喚醒。
if (ws == Node.SIGNAL) {
// 設置“頭節點對應的線程狀態”爲空狀態。失敗的話,則繼續循環。
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue;
// 喚醒“頭節點的下一個節點所對應的線程”。
unparkSuccessor(h);
}
// 如果頭節點對應的線程是空狀態,則設置“文件點對應的線程所擁有的共享鎖”爲其它線程獲取鎖的空狀態。
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
// 如果頭節點發生變化,則繼續循環。否則,退出循環。
if (h == head) // loop if head changed
break;
}
}
一個簡單實例:
package thread;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
class SDTask extends Thread
{
private Semaphore s;
public SDTask(Semaphore s,String name)
{
super(name);
this.s = s;
}
public void run()
{
try
{
System.out.println(Thread.currentThread().getName()+" 嘗試獲取3個信號!!!");
s.acquire(3);
System.out.println(Thread.currentThread().getName()+" 獲取了3個信號!!!");
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e)
{
e.printStackTrace();
}finally
{
System.out.println(Thread.currentThread().getName()+" 釋放了3個信號!!!");
s.release(3);
}
}
}
public class SemaphoreDemo2
{
public static void main(String[] args)
{
Semaphore s = new Semaphore(7);
for(int i=0; i<3; i++)
{
new SDTask(s,"thread"+i).start();;
}
}
}
結果:
thread0 嘗試獲取3個信號!!!
thread2 嘗試獲取3個信號!!!
thread0 獲取了3個信號!!!
thread2 獲取了3個信號!!!
thread1 嘗試獲取3個信號!!!
thread0 釋放了3個信號!!!
thread2 釋放了3個信號!!!
thread1 獲取了3個信號!!!
thread1 釋放了3個信號!!!
總結:
Semaphore並沒有採用Lock進行鎖操作,使用volatile state和for循環,通過修改條件來改變線程的操作。