Java并发编程--wait/notify/notifyAll 方法的使用

1. java.lang.Object#wait()

Causes the current thread to wait until another thread invokes the{@link java.lang.Object#notify()} method or the
{@link java.lang.Object#notifyAll()} method for this object. In other words, this method behaves exactly as if it
simply performs the call {@code wait(0)}. The current thread must own this object's monitor. The thread releases
ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake
up either through a call to the {@code notify} method or the {@code notifyAll} method. The thread then waits until
it can re-obtain ownership of the monitor and resumes execution.As in the one argument version, interrupts and
spurious wake ups are possible, and this method should always be used in a loop:
    synchronized (obj) {
        while (condition does not hold)
            obj.wait();
        ... // Perform action appropriate to condition
    }
This method should only be called by a thread that is the owner of this object's monitor. See the {@code notify} 
method for a description of the ways in which a thread can become the owner of a monitor.

2. java.lang.Object#notify

Wakes up a single thread that is waiting on this object's monitor. If any threads are waiting on this object, one of 
them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation. A thread
waits on an object's monitor by calling one of the {@code wait} methods. The awakened thread will not be able to
proceed until the current thread relinquishes the lock on this object. The awakened thread will compete in the usual
manner with any other threads that might be actively competing to synchronize on this object; for example, the awakened
thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object. This method should
only be called by a thread that is the owner of this object's monitor. A thread becomes the owner of the object's
monitor in one of three ways:
  • By executing a synchronized instance method of that object.
  • By executing the body of a {@code synchronized} statement that synchronizes on the object.
  • For objects of type {@code Class,} by executing a synchronized static method of that class.

 

3.java.lang.Object#notifyAll

Wakes up all threads that are waiting on this object's monitor. A thread waits on an object's monitor by calling one of the {@code wait} methods.
The awakened threads will not be able to proceed until the current thread relinquishes the lock on this object.
The awakened threads will compete in the usual manner with any other threads that might be actively competing to synchronize on this object; for example,
the awakened threads enjoy no reliable privilege or disadvantage in being the next thread to lock this object.
This method should only be called by a thread that is the owner of this object's monitor.
See the {@code notify} method for a description of the ways in which a thread can become the owner of a monitor.

定义一个阻塞队列
import java.util.LinkedList;
import java.util.Queue;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class BlockingQueue<T> {

    private Queue<T> buffer = new LinkedList<>();

    public T pop() throws InterruptedException {
        // 作用于当前对象
        synchronized(this){
            log.info("====>Thread {} 得到了锁", System.identityHashCode(Thread.currentThread()));
            while (buffer.isEmpty()){
                this.wait();
            }
            return buffer.remove();
        }
    }

    public void push(T data){
        synchronized (this){
            buffer.add(data);
            notify();
        }
    }
}

测试

import java.util.concurrent.CountDownLatch;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class BlockingQueueTest {

    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> queue = new BlockingQueue<>();
        CountDownLatch countDownLatch = new CountDownLatch(1);
        // 新建两个线程去队列中获取数据
        Thread t1 = new Thread(()-> {
            try {
                countDownLatch.await();
                String data = queue.pop();
                log.info("====>t1 get data success! data:{}", data);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread t2 = new Thread(()-> {
            try {
                countDownLatch.await();
                String data = queue.pop();
                log.info("====>t2 get data success! data:{}", data);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        log.info("====>step1. t1_{}'s state:{}", System.identityHashCode(t1), t1.getState());
        log.info("====>step1. t2_{}'s state:{}", System.identityHashCode(t2), t2.getState());

        // 启动两个线程,使用 CountDownLatch 控制并发去抢占 push() 方法中的锁。
        t1.setDaemon(true);
        t2.setDaemon(true);
        t1.start();
        t2.start();
        countDownLatch.countDown();
        log.info("====>step2. t1_{}'s state:{}", System.identityHashCode(t1), t1.getState());
        log.info("====>step2. t2_{}'s state:{}", System.identityHashCode(t2), t2.getState());

        // 主线程等待10毫秒,获得锁的线程执行 wait() 方法后释放锁,Blocked 的线程得到了锁并执行了 wait() 方法后进入 waiting 状态。
        Thread.sleep(10L);
        log.info("====>step3. t1_{}'s state:{}", System.identityHashCode(t1), t1.getState());
        log.info("====>step3. t2_{}'s state:{}", System.identityHashCode(t2), t2.getState());

        queue.push("哈哈哈");
        // push() 方法调用 notify() 去唤醒 waiting 状态的线程,因为线程已经在 wait() 方法释放了 monitor 锁,
        // 线程被唤醒后会转到 blocked 状态重新去获取 monitor 锁,得到锁后状态变为 runnable 并执行之后的业务代码。
        log.info("====>step4. t1_{}'s state:{}", System.identityHashCode(t1), t1.getState());
        log.info("====>step4. t2_{}'s state:{}", System.identityHashCode(t2), t2.getState());

        // 主线程等待10毫秒
        Thread.sleep(10L);
        log.info("====>step5. t1_{}'s state:{}", System.identityHashCode(t1), t1.getState());
        log.info("====>step6. t2_{}'s state:{}", System.identityHashCode(t2), t2.getState());

        // java虚拟机(相当于进程)退出的时机是:虚拟机中所有存活的线程都是守护线程。只要还有存活的非守护线程虚拟机就不会退出,
        // 而是等待非守护线程执行完毕;反之,如果虚拟机中的线程都是守护线程,那么不管这些线程的死活java虚拟机都会退出。
        Thread.sleep(1000L);
        log.info("====>main thread's state:{}", Thread.currentThread().getState());

    }
}

运行结果:

 

posted @ 2020-07-11 14:25  景岳  阅读(363)  评论(0编辑  收藏  举报