对java 多线程 wait notify notifyAll 的理解

3个人玩游戏一台手柄游戏,一次只能有一个人玩  示例代码1

public class Player implements Runnable {
    private final int id;
    private Game game;

    public Player(int id, Game game) {
        this.id = id;
        this.game = game;
    }


    public String toString() {
        return "Athlete<" + id + ">";
    }

    public int hashCode() {
        return new Integer(id).hashCode();
    }
   
    public void playGame() throws InterruptedException{
        System.out.println(this.toString() + " ready!");
        game.play(this);
    }

    public void run() {
        try {
            playGame();
        } catch (InterruptedException e) {
            System.out.println(this + " quit the game");
        }
    }
}

public class Game implements Runnable {
    private boolean start = false;

    public void play(Player player) throws InterruptedException {
        synchronized (this) {
        System.out.println(player.toString()+"获得锁");
            while (!start){
            System.out.println(player.toString()+"在等待");
            wait();
            System.out.println( player.toString()+"被唤醒了");
            }
              
            if (start)
                System.out.println(player + " have played!");
        }
    }

    //通知所有玩家
    public synchronized void beginStart() {
        start = true;
        notifyAll();
    }

    public void run() {
        start = false;
        System.out.println("Ready......");
        System.out.println("Ready......");
        System.out.println("game start");
        beginStart();//通知所有玩家游戏准备好了
    }

    public static void main(String[] args) throws InterruptedException {
        Set<Player> players = new HashSet<Player>();
        //实例化一个游戏
        Game game = new Game();
       
        //实例化3个玩家
        for (int i = 0; i < 3; i++)
            players.add(new Player(i, game));
       
        //启动3个玩家
        Iterator<Player> iter = players.iterator();
        while (iter.hasNext())
            new Thread(iter.next()).start();
        Thread.sleep(100);
       
        //游戏启动
        new Thread(game).start();
    }
}

执行结果:
Athlete<0> ready!
Athlete<1> ready!
Athlete<2> ready!
Athlete<0>获得锁
Athlete<0>在等待
Athlete<2>获得锁
Athlete<2>在等待
Athlete<1>获得锁
Athlete<1>在等待
Ready......
Ready......
game start
Athlete<1>被唤醒了
Athlete<1> have played!
Athlete<2>被唤醒了
Athlete<2> have played!
Athlete<0>被唤醒了
Athlete<0> have played!

游戏对象game只有1个,synchronized (this)锁的实际就是game对象, 三个玩家线程都依次得到锁并在wait等待时释放锁。启动游戏线程时,通知所有线程,三个玩家线程需要竞争同步锁,得到同步锁的线程执行完synchronized(this)代码块后,自动释放同步锁,处于同步锁阻塞的剩下的2个线程再次竞争,直至都执行完毕。

总结:
wait()会释放锁,下一次被唤醒并获得锁时,会紧接着wait的下一行开始执行。
obj.notifyAll()是唤醒所有在obj上等待的线程,但是notifyAll 不会释放锁,执行完synchronized 代码块时,才会释放锁。(注意:obj.notifyAll()只是唤醒所有在obj上等待的线程,并不会唤醒应用中无关的线程)
  notify()是通知一个线程,notifyAll()时通知所有线程,如果上面的例子beginStart方法的notifyAll()改为notify,那么就只有一个人能玩游戏,wait状态的线程如果没被唤醒,会一直的等待,即使锁没被占用。
wait() notify() notifyAll()方法必须写在同步方法或者同步块之内,并且同步的锁必须与调用等待方法和通知方法是同一个对象(比如:synchronized(obj1){},那么调用等待方法的也必须是obj1),否则会报java.lang.IllegalMonitorStateException异常,就是因为没有拥有锁导致的。例如上例的beginStart方法,去掉synchronized ,就会报这个错误。

用三个线程打印出连续10次的ABC,要求1个线程打A,1个线程打B,1个线程打C, 示例代码:
public class PrintABC { 
 
    public static Boolean isThreadA = true; 
    public static Boolean isThreadB = false; 
    public static Boolean isThreadC = false; 
 
    public static void main(String[] args) { 
        final  PrintABC abc = new PrintABC(); 
        new Thread(new Runnable() { 
            public void run() { 
                for (int i = 0; i < 10; i++) { 
                    synchronized (abc) { 
                        while(!isThreadA) { 
                            try { 
                                abc.wait(); 
                            } catch (InterruptedException e) { 
                                // TODO Auto-generated catch block 
                                e.printStackTrace(); 
                            } 
                        } 
                            System.out.print("A"); 
                            isThreadA = false; 
                            isThreadB = true; 
                            isThreadC = false; 
                            abc.notifyAll(); 
                    } 
                } 
            } 
        }).start(); 
 
        new Thread(new Runnable() { 
            public void run() { 
                for (int i = 0; i < 10; i++) { 
                    synchronized (abc) { 
                        while(!isThreadB) { 
                            try { 
                                abc.wait(); 
                            } catch (InterruptedException e) { 
                                // TODO Auto-generated catch block 
                                e.printStackTrace(); 
                            } 
                        } 
                            System.out.print("B"); 
                            isThreadA = false; 
                            isThreadB = false; 
                            isThreadC = true; 
                            abc.notifyAll(); 
                    } 
                } 
            } 
        }).start(); 
         
        new Thread(new Runnable() { 
            public void run() { 
                for (int i = 0; i < 10; i++) { 
                    synchronized (abc) { 
                        while(!isThreadC) { 
                            try { 
                                abc.wait(); 
                            } catch (InterruptedException e) { 
                                // TODO Auto-generated catch block 
                                e.printStackTrace(); 
                            } 
                        } 
                            System.out.print("C"); 
                            isThreadA = true; 
                            isThreadB = false; 
                            isThreadC = false; 
                            abc.notifyAll(); 
                    } 
                } 
            } 
        }).start(); 
    } 
}
执行结果:
ABCABCABCABCABCABCABCABCABCABC

abc对象是三个线程共同的锁,存在竞争。for循环中刚好可以使每个字母打印10次,for循环每次都需要竞争abc锁,竞争得到abc锁时,条件不符合则等待并释放锁,让其他线程竞争,条件符合则打印字母,打印完成后,会通知所有的线程,在执行完synchronized 块时,会释放锁。
注意:notifyAll 和wait方法的调用对象是锁,所以写成abc.notifyAll();abc.wait(); 3人玩游戏的那个例子,之所以能不写调用的对象,是因为锁就是this ,不写也是等价。


猜你喜欢

转载自mytdyhm123456.iteye.com/blog/2344869