zoukankan      html  css  js  c++  java
  • 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());
    
        }
    }

    运行结果:

  • 相关阅读:
    PowerDesigner使用教程 —— 概念数据模型 (转)
    面板Panel:1.认识Ext.Panel(转)
    批量替换一个数据库中所有表中所有记录
    Ext.data库中几个常用类的原理及其使用 (转)
    也谈C#.NET防止SQL注入式攻击
    jQuery1.3新特性深度分析(Event)
    Sql实现Split
    SQL Server 清除数据库日志脚本(转)
    ExtJS之面向对象编程基本知识
    实战 SQL Server 2005 镜像配置
  • 原文地址:https://www.cnblogs.com/xxoome/p/13283463.html
Copyright © 2011-2022 走看看