以填酒、喝酒为例说明线程同步:阻塞、死锁、非阻塞、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;

    /**
     * 填酒,阻塞式地
     * 
     * @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不会空转
         */
    
}

Leave a Comment

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.