美文网首页JUC原理收藏-技术篇
并发编程-(2)-Object.wait()、Object.no

并发编程-(2)-Object.wait()、Object.no

作者: tianlang136520 | 来源:发表于2019-10-27 22:24 被阅读0次
    死神---朽木露琪亚

    目录:

    • 1、方法介绍:
      • 1.1、wait()、notify()
      • 1.2、join()方法
      • 1.3、sleep()方法
    • 2、实例demo:
      • 2.1、wait()、notify()的demo
      • 2.2、wait()、notify() 原理时序图
      • 2.3、join()的demo
      • 2.4、sleep()的demo
    • 3、wait()、notify()的时序图
    • 4 、源码分析:
      • 4.1、wait()分析
      • 4.2、notify()分析
      • 4.3、join()分析
    • 5 、总结:

    1、方法介绍

    1.1、wait()、notify()方法:
    • 1、wait、notify以及notifyAll都是Object对象的方法,他们必须在被 synchronized 同步的方法或代码块中调用,否则会报错。
    • 2、调用wait方法会使该线程进入等待状态,并且会释放被同步对象的锁
    • 3、notify操作可以唤醒一个因执行wait而处于阻塞状态的线程,使其进入就绪状态,被唤醒的线程会去尝试着获取对象锁,然后执行wait之后的代码。如果发出notify操作时,没有线程处于阻塞状态,那么该命令会忽略。注意执行notify并不会马上释放对象锁,会等到执行完该同步方法或同步代码块后才释放
    • 4、notifyAll方法可以唤醒等待队列中等待同一共享资源的“全部”线程从等待状态--->就绪状态,进行锁争夺,类似第3天后面逻辑。
    1.2、join()方法:
    • 1、jdk7中对join()定义:suspends the execution of the calling thread until the object called finishes its execution。(暂停当前正在执行的线程,直到join的线程结束)
    1.3、sleep()方法:

    后补~

    2、实例demo

    2.1、wait()、notify()的demo
    /**
     * @program: jvmproject
     * @description: 线程的wait方法和notify方法
     * @author: biudefu
     * @create: 2019-07-28
     **/
    public class WaitAndNotifyMain {
    
        static boolean flag = true;
        static Object lock = new Object();
    
        public static void main(String[] args) throws Exception {
    
            Thread waitThread = new Thread(new Wait(), "WaitThread");
            waitThread.start();
    
            TimeUnit.SECONDS.sleep(1);
    
            Thread notifyThread = new Thread(new Notify(), "NotifyThread");
            notifyThread.start();
    
        }
    
        static class Wait implements Runnable {
            public void run() {
                // 加锁,拥有lock的Monitor
                synchronized (lock) {
                    // 当条件不满足时,继续wait,同时释放了lock的锁
                    while (flag) {
                        try {
                            System.out.println(Thread.currentThread().getName() + " flag is true. wait @ "
                                    + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                            lock.wait();
                            System.out.println(Thread.currentThread().getName() +" 被唤醒!@ "
                                    + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                        } catch (InterruptedException e) {
                        }
                    }
                    // 条件满足时,完成工作
                    System.out.println(Thread.currentThread().getName() + " flag is false. running @ "
                            + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                    SleepUtils.second(5);
                }
                System.out.println(Thread.currentThread().getName() + " 结束🔚 @ " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            }
        }
    
        static class Notify implements Runnable {
            public void run() {
                // 加锁,拥有lock的Monitor
                synchronized (lock) {
                    // 获取lock的锁,然后进行通知,通知时不会释放lock的锁,
                    // 直到当前线程释放了lock后,WaitThread才能从wait方法中返回
                    System.out.println(Thread.currentThread().getName() + " hold lock. notify @ " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                    lock.notifyAll();
                    flag = false;
                    SleepUtils.second(4);
                }
                System.out.println(Thread.currentThread().getName() + " 释放lock @ " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                SleepUtils.second(6);
                // 再次加锁
                synchronized (lock) {
                    System.out.println(Thread.currentThread().getName() + " hold lock again. sleep @ "
                            + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                    SleepUtils.second(5);
                }
                System.out.println(Thread.currentThread().getName() + " 结束🔚 @ " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            }
        }
    }
    

    运行结果:


    运行结果图
    2.2、wait()、notify() 原理时序图
    时序图
    2.3、join()的demo
    /**
     * @program: jvmproject
     * @description: 剖析Thread中的join方法
     * @author: biudefu
     * @create: 2019-08-28
     **/
    public class JoinMain {
    
        public static void main(String[] args) throws Exception {
            Thread previous = Thread.currentThread();
            for (int i = 0; i < 10; i++) {
                // 每个线程拥有前一个线程的引用,需要等待前一个线程终止,才能从等待中返回
                Thread threadA = new Thread(new Domino(previous), String.valueOf(i));
                threadA.start();
                previous = thread;
            }
    
            TimeUnit.SECONDS.sleep(5);
            System.out.println(Thread.currentThread().getName() + " terminate.");
        }
    
        static class Domino implements Runnable {
            private Thread thread;
    
            public Domino(Thread thread) {
                this.thread = thread;
            }
    
            public void run() {
                try {
                    thread.join();
                } catch (InterruptedException e) {
                }
                System.out.println(Thread.currentThread().getName() + " terminate.");
            }
        }
    
    }
    

    运行结果:


    join运行结果

    每个线程终止的前提是前驱线程终止,才会从join返回。

    2.4、sleep()的demo

    后补

    4 、源码分析

    4.1、wait()&notify()方法分析

    monitor <<<<<传送门

    4.2、join()方法分析
       Thread.java中:
    
        public final void join() throws InterruptedException {
            join(0);
        }
        
        public final synchronized void join(long millis) throws InterruptedException {
            long base = System.currentTimeMillis();
            long now = 0;
    
            if (millis < 0) {
                throw new IllegalArgumentException("timeout value is negative");
            }
    
            if (millis == 0) {  //这个分支是无限期等待直到b线程结束。
                while (isAlive()) {
                    wait(0);  //wait操作,那必然有synchronized与之对应。--->synchronized void join(long millis)
                }
            } else {    //这个分支是等待固定时间,如果b没结束,那么就不等待了。
                while (isAlive()) {
                    long delay = millis - now;
                    if (delay <= 0) {
                        break;
                    }
                    wait(delay);
                    now = System.currentTimeMillis() - base;
                }
            }
        }
    
        Object.java中:
        public final native void wait(long timeout) throws InterruptedException;
    
    分析:

           join()底层也是通过jdk的 native wait()来实现的。
    注意这个wait()方法是Object类中的方法,再来看sychronized锁是哪个线程对象的?

    public final synchronized void join(long millis) throws InterruptedException { ... }
    

            成员方法加了synchronized说明是synchronized(this),this是谁啊?this就是threadA线程对象本身。也就是说,主线程持有了threadA这个对象的锁。
    有了wait(),必然有notify(),什么时候才会notify呢?在jvm源码里:
    hotspot/src/share/vm/runtime/thread.cpp

    void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
      ......
      // Notify waiters on thread object. This has to be done after exit() is called
      // on the thread (if the thread is the last thread in a daemon ThreadGroup the
      // group should have the destroyed bit set before waiters are notified).
      ensure_join(this);
      .......
    }
    
    static void ensure_join(JavaThread* thread) {
      // We do not need to grap the Threads_lock, since we are operating on ourself.
      Handle threadObj(thread, thread->threadObj());
      assert(threadObj.not_null(), "java thread object must exist");
      ObjectLocker lock(threadObj, thread);
      // Ignore pending exception (ThreadDeath), since we are exiting anyway
      thread->clear_pending_exception();
      // Thread is exiting. So set thread_status field in  java.lang.Thread class to TERMINATED.
      java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
      // Clear the native thread instance - this makes isAlive return false and allows the join()
      // to complete once we've done the notify_all below
      java_lang_Thread::set_thread(threadObj(), NULL);
        // 此处看这一句
        // thread就是当前线程,是啥?就是刚才例子中说的threadA线程啊。
      lock.notify_all(thread);
      // Ignore pending exception (ThreadDeath), since we are exiting anyway
      thread->clear_pending_exception();
    }
    

    线程threadA执行完毕的时候,jvm会自动唤醒阻塞在threadA对象上的线程,在我们的demo中也就是主线程Main。至此,threadA线程对象被notifyall了,那么主线程也就能继续跑下去了。

    join()过程:join() 是一个synchronized方法, 底层调用了wait(),目的是让持有这个同步锁的线程进入等待队列,那么谁持有了这个同步锁呢?答案是主线程,因为主线程调用了threadA.join()方法,相当于在threadA.join()代码这块写了一个同步代码块,谁去执行了这段代码呢,是主线程,所以主线程被wait()了。然后在子线程threadA执行完毕之后,JVM会调用lock.notify_all(thread);唤醒持有threadA这个对象锁的线程,也就是主线程,会继续执行。

    4.3、sleep()方法分析

    后补~

    总结:

    名称 join() wait() notify() sleep()
    底层实现 wait() XXX XXX XXX
    释放锁 释放锁 不释放 不释放

    参考资料:

    《深入理解Java虚拟机-2nd》
    《Java 并发编程实战》
    《Java 并发编程的艺术》

    相关文章

      网友评论

        本文标题:并发编程-(2)-Object.wait()、Object.no

        本文链接:https://www.haomeiwen.com/subject/asydectx.html