一个填酒线程,一个喝酒线程对一个酒杯轮流进行操作。
阻塞、死锁
package player.kent.chen.learn.threads.notify;
/**
* 酒杯
*
* @author kent 2013-3-11下午4:34:30
*/
public class Cup {
private volatile boolean empty = true;
/**
* 填酒,阻塞式地
*
* @author kent 2013-3-11下午4:59:33
*/
public synchronized void fill() {
while (!empty) { //若酒杯不为空则等待
;
}
doFill();
}
/**
* 清空酒杯,阻塞式地
*
* @author kent 2013-3-11下午4:59:50
*/
public synchronized void drain() {
while (empty) {//若酒杯为空则等待
;
}
doDrain();
}
private void doFill() {
System.out.println(Thread.currentThread().getName() + " filled the cup");
empty = false;
}
private void doDrain() {
System.out.println(Thread.currentThread().getName() + " drained the cup");
empty = true;
}
}
package player.kent.chen.learn.threads.notify;
/**
* 酒吧工作人员,无休止地填酒 (阻塞式地)
*/
public class BlockBartender implements Runnable {
private Cup cup;
/**
* @param cup
*/
public BlockBartender(Cup cup) {
super();
this.cup = cup;
}
public void run() {
while (true) {
cup.fill();
}
}
}
package player.kent.chen.learn.threads.notify;
/**
* 喝酒的人,不停地喝酒(阻塞式地)
*
* @author kent 2013-3-11下午5:02:34
*/
public class BlockDrinker implements Runnable {
private Cup cup;
/**
* @param cup
*/
public BlockDrinker(Cup cup) {
super();
this.cup = cup;
}
/**
* @author kent 2013-3-11下午5:02:56
*/
public void run() {
while (true) {
cup.drain();
}
}
}
package player.kent.chen.learn.threads.notify;
public class CupMain {
public static void main(String[] args) {
Cup cup = new Cup();
Thread bartenderThread = new Thread(new BlockBartender(cup), "bartender");
Thread drinkerThread = new Thread(new BlockDrinker(cup), "drinker");
bartenderThread.start();
drinkerThread.start();
/**
* 执行时将以死锁告终。 <br/>
* 1. 假设bartender先执行并先获得锁 <br/>
* 2. bartender填酒, 然后empty=false <br/>
* 3. 然后bartender陷入等待,直到drinker把empty置为true <br/>
* 4. 同时bartender一直持有锁 <br/>
* 5. drinker无法将empty置为false, 因为它在执行drain()时发现自己拿不到锁 <br/>
* 6. 结果,bartender要drinker把empty置为true后才能释放锁,
* 而drinker要bartender释放锁后才能将empty置为true。结果就是死锁
*/
}
}
非阻塞、无死锁
package player.kent.chen.learn.threads.notify;
/**
* 酒杯
*
* @author kent 2013-3-11下午4:34:30
*/
public class Cup {
private volatile boolean empty = true;
/**
* 非阻塞式地填酒
*
* @author kent 2013-3-11下午4:59:59
*/
public synchronized void tryFill() {
if (empty) {
doFill();
}
}
/**
* 非阻塞式地清空酒杯
*
* @author kent 2013-3-11下午5:00:12
*/
public synchronized void tryDrain() {
if (!empty) {
doDrain();
}
}
private void doFill() {
System.out.println(Thread.currentThread().getName() + " filled the cup");
empty = false;
}
private void doDrain() {
System.out.println(Thread.currentThread().getName() + " drained the cup");
empty = true;
}
}
package player.kent.chen.learn.threads.notify;
/**
* 酒吧工作人员,无休止地填酒 (非阻塞式地)
*/
public class NonBlockBartender implements Runnable {
private Cup cup;
/**
* @param cup
*/
public NonBlockBartender(Cup cup) {
super();
this.cup = cup;
}
public void run() {
while (true) {
cup.tryFill();
}
}
}
package player.kent.chen.learn.threads.notify;
/**
* 喝酒的人,不停地喝酒(非阻塞式地)
*
* @author kent 2013-3-11下午5:02:34
*/
public class NonBlockDrinker implements Runnable {
private Cup cup;
/**
* @param cup
*/
public NonBlockDrinker(Cup cup) {
super();
this.cup = cup;
}
/**
* @author kent 2013-3-11下午5:02:56
*/
public void run() {
while (true) {
cup.tryDrain();
}
}
}
package player.kent.chen.learn.threads.notify;
public class CupMain {
public static void main(String[] args) {
Cup cup = new Cup();
Thread bartenderThread = new Thread(new NonBlockBartender(cup), "bartender");
Thread drinkerThread = new Thread(new NonBlockDrinker(cup), "drinker");
bartenderThread.start();
drinkerThread.start();
/**
* 执行时将出现预期的结果:一填一喝,一喝一填 <br/>
* 1. 假设bartender先执行并先获得锁 <br/>
* 2. bartender填酒, 然后empty=false <br/>
* 3. 然后bartender再试着填酒,发现empty仍是false,所以退出tryFill()方法并释放锁<br/>
* 4. 可能重复第3步若干次 <br/>
* 5. drinker在某个时机获得锁,然后empty=true<br/>
* 6. 然后drinker再试着喝酒,发现empty仍是true,所以退出tryDrain()方法并释放锁<br/>
* 7. 可能重复第6步若干次<br/>
* 8. bartender在某个时机获得锁,然后回到第2步。。。
* 这种机制的缺点是里面的while循环会不停轮询empty状态,造成cpu浪费
*/
}
}
使用Notify-Wait机制,避免轮询
package player.kent.chen.learn.threads.notify;
/**
* 酒杯
*
* @author kent 2013-3-11下午4:34:30
*/
public class Cup {
private volatile boolean empty = true;
/**
* 填酒,使用Notify-Wait机制
*
* @author kent 2013-3-11下午4:59:33
* @throws InterruptedException
*/
public synchronized void fillWithNw() throws InterruptedException {
while (!empty) { //若酒杯不为空则等待并释放锁
wait();
}
doFill();
notifyAll(); //把等待中的其他线程唤醒(实际就是清空酒杯的线程)
}
/**
* 清空酒杯,使用Notify-Wait机制
*
* @author kent 2013-3-11下午4:59:50
* @throws InterruptedException
*/
public synchronized void drainWithNw() throws InterruptedException {
while (empty) {//若酒杯为空则等待并释放锁
wait();
}
doDrain();
notifyAll(); //把等待中的其他线程唤醒(实际就是填酒的线程)
}
private void doFill() {
System.out.println(Thread.currentThread().getName() + " filled the cup");
empty = false;
}
private void doDrain() {
System.out.println(Thread.currentThread().getName() + " drained the cup");
empty = true;
}
}
package player.kent.chen.learn.threads.notify;
/**
* 酒吧工作人员,无休止地填酒 (利用Notify + Wait机制与喝酒者互动)
*/
public class NwBartender implements Runnable {
private Cup cup;
/**
* @param cup
*/
public NwBartender(Cup cup) {
super();
this.cup = cup;
}
public void run() {
while (true) {
try {
cup.fillWithNw();
} catch (InterruptedException e) {
//do nothing
}
}
}
}
package player.kent.chen.learn.threads.notify;
/**
* 喝酒的人,不停地喝酒(利用Notify + Wait机制与酒吧招待互动)
*
* @author kent 2013-3-11下午5:02:34
*/
public class NwDrinker implements Runnable {
private Cup cup;
/**
* @param cup
*/
public NwDrinker(Cup cup) {
super();
this.cup = cup;
}
/**
* @author kent 2013-3-11下午5:02:56
*/
public void run() {
while (true) {
try {
cup.drainWithNw();
} catch (InterruptedException e) {
//do nothing
}
}
}
}
package player.kent.chen.learn.threads.notify;
public class CupMain {
public static void main(String[] args) {
Cup cup = new Cup();
Thread bartenderThread = new Thread(new NwBartender(cup), "bartender");
Thread drinkerThread = new Thread(new NwDrinker(cup), "drinker");
bartenderThread.start();
drinkerThread.start();
}
/**
* 执行时将出现预期的结果,而且没有CPU不会空转
*/
}