一、条件队列
1. 标准形式:
void stateDependentMethod() throws InterruptedException{
synchronized(lock) { //首先要持有锁
while(!conditionPredicate()) //在唤醒后需再次确认,以防止在唤醒和获取锁之间的时间状态再次改变
lock.wait(); //wait会释放锁,等待唤醒。在唤醒前,wait会尝试去获取锁,和普通调用竞争优先级相同
}
}
Ex.
public class BoundedBuffer<V> extends BaseBoundedBuffer<V>{
public BoundedBuffer(int size) { super(size); }
public synchronized void put(V v) throws InterruptedException{
while(isFull()){
System.out.println(Thread.currentThread().getName()+ " put我睡了");
wait();
System.out.println(Thread.currentThread().getName()+ " put醒来了");
}
doPut(v);
notifyAll();
}
public synchronized V take() throws InterruptedException{
while(isEmpty()){
System.out.println(Thread.currentThread().getName()+ " take我睡了");
wait();
System.out.println(Thread.currentThread().getName() + " take醒来了");
}
V v = doTake();
notifyAll();
return v;
}
public static void main(String[] args) throws InterruptedException{
final BoundedBuffer<Integer> buf = new BoundedBuffer<Integer>(10);
new Thread(new Runnable(){
public void run(){
try {
buf.take();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable(){
public void run(){
try {
buf.take();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
Thread.sleep(2000);
new Thread(new Runnable(){
public void run(){
try {
buf.put(1);;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
}
/** 上面的代码输出结果为 :
* Thread-0 take我睡了
* Thread-1 take我睡了
* Thread-1 take醒来了
* Thread-0 take醒来了
* Thread-0 take我睡了
**/
由于Thread-0 获取锁时没有竞争过Thread-1 ,所以Thread-1先运行,当Thread-0获取到锁时,经过判断条件,发现数组又是空了,所以又睡了。
- 使用条件队列,必须要满足 锁、条件谓词和条件变量之间的三元关系
- 使用显式条件变量的有界缓存
@ThreadSafe
public class ConditionBoundedBuffer<T>{
protected final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
@GuardedBy("lock")
private T[] items = (T[]) new Object[BUFFER_SIZE];
@GuardedBy("lock")
private int head,tail,count;
public void put(T item) throws InterruptedException{
lock.lock();
try{
while(count == items.length)
notFull.await();
items[tail] = item;
if(++tail == items.length)
tail = 0;
++count;
notEmpty.signal();
}finally{
lock.unlock();
}
}
public T take() throws InterruptedException{
lock.lock();
try{
while(count == 0)
notEmpty.await();
T x = items[head];
items[head] = null;
if(++head == items.length)
head = 0;
-- count;
notFull.signal();
return x;
}finally{
lock.unlock();
}
}
}
4.信号量与lock
信号量与lock很类似,比如lock的lock类似于semaphore的acquire,lock的unlock类似于semaphore的release。其实它们(包括很多同步类)都是基于AQS(AbstractQueuedSynchronizer)
//使用锁实现信号量
@ThreadSafe
public class SemaphoreOnLock{
private final Lock lock = new ReentrantLock();
private final Condition permitsAvailable = lock.newCondition();
@GuardedBy("lock") private int permits;
public SemaphoreOnLock(int initPermits){
lock.lock();
try{
permits = initPermits;
}finally{
lock.unlock();
}
}
public void acquire() throws InterruptedException{
lock.lock();
try{
while(permits == 0)
permitsAvailable.await();
--permits;
}finally{
lock.unlock();
}
}
public void release() throws InterruptedException{
lock.lock();
try{
++permits;
permitsAvailable.signal();
}finally{
lock.unlock();
}
}
}
5.AbstractQueuedSynchronizer
//使用AQS实现二元闭锁
@ThreadSafe
public class OneShotLatch{
private final Sync sync = new Sync();
public void signal() {sync.releaseShared(0);}
public void await() throws InterruptedException{
sync.acquireSharedInterruptibly(0);
}
private class Sync extends AbstractQueuedSynchronized{
protected int tryAcquiredShared(int ignored){
return (getState() == 1) ? 1 : -1;
}
protected boolean tryReleaseShared(int ignored){
setState(1);
return true;
}
}
}
实现原理:
sync.releaseShared(0) 实现源码是:
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) { //在这个例子中仅仅将可用状态设为1,并返回true
doReleaseShared();
return true;
}
return false;
}
doReleaseShared()即对于链表中待唤醒的线程(即state的值为-1,全部唤醒),实现如下:
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) // 将链表中所有值为-1的线程唤醒
continue; // loop to recheck cases
unparkSuccessor(h); //唤醒关键步骤,使用unpark方法
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break; //双向链表
}
}
LockSupport .park()和 LockSupport .unpark()实现线程的阻塞和唤醒 的
park的小例子:http://www.tuicool.com/articles/MveUNzF
sync.acquireSharedInterruptibly(0)的实现源码是:
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
if (Thread.interrupted()) //响应中断
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
doAcquireSharedInterruptibly一旦链表的首节点状态发生改变,就通知第二个节点,并且通知与之shared的节点(闭锁是共享锁)。源码如下:
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
//将当前线程包装为类型为Node.SHARED的节点,标示这是一个共享节点。
final Node node = addWaiter(Node.SHARED); //添加新节点
boolean failed = true;
try {
for (;;) {
//如果新建节点的前一个节点,就是Head,说明当前节点是AQS队列中等待获取锁的第一个节点,
//按照FIFO的原则,可以直接尝试获取锁。
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
//获取成功,需要将当前节点设置为AQS队列中的第一个节点,这是AQS的规则//队列头
//节点表示正在获取锁的节点
setHeadAndPropagate(node, r);
p.next = null; // 将原先的头结点回收,help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) && //检查下是否需要将当前节点挂起
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
parkAndCheckInterrupt实现
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this); //等待获取权限,阻塞
return Thread.interrupted();
}
- ReentrantLock 实现
//非公平的ReentrantLock实现tryAcquire
//reentrantLock是独占锁
protected boolean tryAcquire(int ignored){
final Thread thread = Thread.currentThread();
int c = getState();
if(c == 0){ //锁还未被占有
if(compareAndSetState(0,1)){ // 防止锁的状态又发生更改
owner = current;
return true;
}
}else if(current == owner) { //锁的占用者是自己
setState(c+1); //把次数加一
return true;
}
return false; //获取失败
}
- Semaphore
信号量是共享锁
1. 计算剩余的许可数量
2. 如果没有足够的许可,翻译失败
3. 如果有剩余,采用compareAndSetState()以原子方法降低许可技术
protected int tryAcquireShared(int acquires){
while(true){
int available = getState();
int remaining = available - acquires;
//这样可以减少进行原子操作的次数
if (remaining < 0 || compareAndSetState(available,remaining))
return remaining;
}
}