一个填酒线程,一个喝酒线程对一个酒杯轮流进行操作。
阻塞、死锁
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不会空转 */ }