Java线程的wait(), notify()和notifyAll()

Java线程生命周期

类java.lang.Thread包含一个静态的State enum用于定义每种可能的状态. 在任意的时间点, 线程会处于以下的状态之一:

NEW – 新创建的线程, 还未启动(在调用 start() 之前的状态). A thread that has not yet started is in this state.
RUNNABLE – 正在运行或已经准备好运行但是在等待资源. either running or ready for execution but it’s waiting for resource allocation
BLOCKED – 在等待获取一个monitor lock来进入或重新进入同步的代码块或方法 waiting to acquire a monitor lock to enter or re-enter a synchronized block/method
WAITING – 在等待其他线程执行一个特定的操作, 没有时间限制 waiting for some other thread to perform a particular action without any time limit
由这三种方法之一进入 
object.wait()
thread.join()
LockSupport.park()

TIMED_WAITING – 在某个时间限制内, 等待其他线程执行一个特定的操作 waiting for some other thread to perform a specific action for a specified period
由这五种方法之一进入 
thread.sleep(long millis)
wait(int timeout) or wait(int timeout, int nanos)
thread.join(long millis)
LockSupport.parkNanos
LockSupport.parkUnti

TERMINATED – 运行已经结束或意外终止 has completed its execution

 

wait(), notify()和notifyAll()方法用于在线程间建立关联. 在对象上调用wait()将使线程进入WAITTING状态, 直到其他线程对同一个对象调用notify()或notifyAll(). 在任何线程上, 对一个对象调用wait(), notify()和notifyAll(), 都需要先获得这个对象的锁, 就是说, 这些方法必须在synchronized方法或代码块中调用.

wait()

在对象上调用wait(), 会使当前线程进入等待状态, 直至另一个线程对这个对象调用了notify() 或notifyAll() 方法. 换句话说, 就是简单的执行了wait(0)方法.

在调用wait()时, 当前线程必须拥有这个对象的monitor. 调用后, 线程会释放对象的monitor, 并一直等待直到另一个线程通过notify()或notifyAll()唤醒正在等待这个对象的monitor的线程们. 然后线程会一直等待直到重新拿回这个对象的monitor, 然后才能继续运行. 

对于带参数的版本, 中断和唤醒都是可能的, 这个方法必须在一个循环中使用.

 synchronized (obj) {
     while (<condition does not hold>)
         obj.wait();
     ... // Perform action appropriate to condition
 }

.

notify()

唤醒正在等待这个对象的monitor的线程中的一个. 如果有多个线程正在等待, 会选择其中的一个, 这个选择是任意的, 因具体实现而不同. 唤醒的线程并不会立即继续执行, 它需要获得这个对象的锁之后才能继续执行. 唤醒的对象将与其他正在竞争这个对象的锁的线程一起竞争对象的锁. 

这个方法只允许在拥有当前对象的monitor的线程上调用, 一个线程要得到对象的monitor, 只能通过以下三种方法之一:

  • 执行这个对象的一个同步方法
  • 执行这个对象中的一段同步代码
  • 对于type类型的对象, 执行这个类的一个静态同步方法

在同一时刻, 只允许一个线程得到一个对象的monitor

调用notify()时, 在所有WAITING状态的线程中只会有一个线程被通知, 这个选择是随机的, 被通知的线程并不会立即得到对象的锁, 而是一直等到调用notify()的线程释放锁, 在这之前线程都是BLOCKED状态. 当获得锁后, 就会从BLOCKED状态变为RUNNING状态. 例子

class Shared {
    synchronized void waitMethod() {
        Thread t = Thread.currentThread();
        System.out.println(t.getName() + " is releasing the lock and going to wait");
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(t.getName() + " has been notified and acquired the lock back");
    }

    synchronized void notifyOneThread() {
        Thread t = Thread.currentThread();
        notify();
        System.out.println(t.getName() + " has notified one thread waiting for this object lock");
    }
}

public class MainClass {
    public static void main(String[] args) {
        final Shared s = new Shared();
        //Thread t1 will be waiting for lock of object 's'
        Thread t1 = new Thread() {
            @Override
            public void run() {
                s.waitMethod();
            }
        };
        t1.start();

        //Thread t2 will be waiting for lock of object 's'
        Thread t2 = new Thread() {
            @Override
            public void run() {
                s.waitMethod();
            }
        };
        t2.start();

        //Thread t3 will be waiting for lock of object 's'
        Thread t3 = new Thread() {
            @Override
            public void run() {
                s.waitMethod();
            }
        };
        t3.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //Thread t4 will notify only one thread which is waiting for lock of object 's'
        Thread t4 = new Thread() {
            @Override
            public void run() {
                s.notifyOneThread();
            }
        };
        t4.start();
    }
}

.

notifyAll()

当线程在对象上调用notifyAll()时, 所有WAITING状态的线程都会被唤醒, 所有的线程都会从WAITING状态变成BLOCKED状态, 然后争抢对象的锁. 得到对象锁的线程, 将变成RUNNING状态, 而其他线程则继续保持BLOCKED状态继续等待获取对象锁. 例子

class Shared {
    synchronized void waitMethod() {
        Thread t = Thread.currentThread();
        System.out.println(t.getName() + " is releasing the lock and going to wait");
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(t.getName() + " has been notified and acquired the lock back");
    }

    synchronized void notifyAllThread() {
        Thread t = Thread.currentThread();
        notifyAll();
        System.out.println(t.getName() + " has notified all threads waiting for this object lock");
    }
}

public class MainClass {
    public static void main(String[] args) {
        final Shared s = new Shared();
        //Thread t1 will be waiting for lock of object 's'
        Thread t1 = new Thread() {
            @Override
            public void run() {
                s.waitMethod();
            }
        };
        t1.start();

        //Thread t2 will be waiting for lock of object 's'
        Thread t2 = new Thread() {
            @Override
            public void run() {
                s.waitMethod();
            }
        };
        t2.start();

        //Thread t3 will be waiting for lock of object 's'
        Thread t3 = new Thread() {
            @Override
            public void run() {
                s.waitMethod();
            }
        };
        t3.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //Thread t4 will notify all threads which are waiting for lock of object 's'
        Thread t4 = new Thread() {
            @Override
            public void run() {
                s.notifyAllThread();
            }
        };
        t4.start();
    }
}

.

一个生产者和消费者的例子

注意, 在1个生产1个消费的情况下, 是能确保生产和消费的互相通知的, 但是在2个生产1个消费的情况下, 有可能要多次notify后消费线程才能拿到queue的锁.

public class DemoThreadWait1 {
    Queue<Integer> queue = new LinkedList<>();

    public void consume() {
        synchronized (queue) {
            while (queue.isEmpty()) {
                try {
                    System.out.println("consume wait");
                    queue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("remove all");
                queue.clear();
                queue.notify();
            }
        }
    }

    public void produce(int i) {
        synchronized (queue) {
            if (queue.size() < 5) {
                System.out.println("add " + i);
                queue.add(i);
            }
            if (queue.size() >= 5) {
                queue.notify();
                try {
                    System.out.println("produce wait");
                    queue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        DemoThreadWait1 demo = new DemoThreadWait1();
        new Thread(()->{
            while(true) {
                demo.consume();
            }
        }).start();

        new Thread(()->{
            while(true) {
                demo.produce((int) (Math.random() * 1000));
            }
        }).start();

        new Thread(()->{
            while(true) {
                demo.produce((int) (Math.random() * 1000));
            }
        }).start();
    }
}

  

posted on 2019-05-18 01:48  Milton  阅读(2527)  评论(0编辑  收藏  举报

导航