• synchronized块中的wait()、nofity()、nofityAll()方法


    前言

      在Java并发编程实战,会经常遇到多个线程访问同一个资源的情况,这个时候就需要维护数据的一致性,否则会出现各种数据错误,其中一种同步方式就是利用Synchronized关键字执行锁机制,锁机制是先给共享资源上锁,只有拿到锁的线程才可以访问共享资源,其他线程进入等待状态。下面将以实例代码讲解一下

    一、wait()、nofity()、nofityAll()讲解

      示例代码

    package thread;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.concurrent.TimeUnit;
    
    /**
     * Created by StoneGeek on 2018/5/19.
     * 博客地址:http://www.cnblogs.com/sxkgeek
     * 当线程执行wait()时,会把当前的锁释放,然后让出CPU,进入等待状态。
     * 当线程执行notify()/notifyAll()方法时,会唤醒一个处于等待状态该对象锁的线程,然后继续往下执行,直到执行完退出对象锁锁住的区域(synchronized修饰的代码块)后再释放锁
     * 个人认为synachronized(){}执行完后会释放锁
     */
    public class WaitNotify {
        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) {
                        System.out.println(Thread.currentThread().getName()
                                + " flag is true. wait@ "
                                + new SimpleDateFormat("HH:mm:ss")
                                .format(new Date()));
                        try {
                            lock.wait();
                            System.out.println("此处继续执行"+Thread.currentThread().getName());
    //                        flag=true;
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    // 条件满足时,完成工作
                    System.out.println(Thread.currentThread().getName()
                            + " flag is false. running@ "
                            + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                }
                synchronized (lock){
                    System.out.println(Thread.currentThread().getName()+"执行结束");
                }
            }
        }
    
        // wait()会立刻释放synchronized(obj)中的obj锁,以便其他线程可以执行obj.notify()
        // 但是notify()不会立刻立刻释放sycronized(obj)中的obj锁,必须要等notify()所在线程执行完synchronized(obj)块中的所有代码才会释放这把锁.
        // yield(),sleep()不会释放锁
        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;
                }
                // 再次加锁
                synchronized (lock) {
                    System.out.println(Thread.currentThread().getName()
                            + " hold lock again. sleep@ "
                            + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                }
                synchronized (lock){
                    System.out.println(Thread.currentThread().getName()+"执行结束");
                }
            }
        }
    }

      执行结果如下

    1  WaitThread flag is true. wait@ 20:50:39
    2  NotifyThread hold lock. notify @ 20:50:40
    3  NotifyThread hold lock again. sleep@ 20:50:40
    4  NotifyThread执行结束
    5  此处继续执行WaitThread
    6  WaitThread flag is false. running@ 20:50:40
    7  WaitThread执行结束

      解释:

      首先创建一个lock对象,然后给这个lock上锁来对多个进程同步,flag是一个标志,用来跳出while循环。

         当线程执行wait()时,会把当前的锁释放,然后让出CPU,进入等待状态。

      此时轮到notifythread线程,并且执行notifyAll(),这个意思是能够唤醒所有正在等待这个lock对象的monitor的线程,但是

      必须要等notify()所在线程执行完synchronized(obj)块中的所有代码才会释放这把锁,

      此时接着waitthread被唤醒,继续执行while循环,执行完之后,由于flag在notifythread中置为false,所以跳出while循环(如果在实例代码的wail()后加flag=true结果是截然不同,由于notirythread进程执行完,此时会一直陷入wait,大家可以试试),

      执行console打印5 6 7

      notify()与notifyAll()的区别 

      notify()方法能够唤醒一个正在等待该对象的monitor的线程,当有多个线程都在等待该对象

      的monitor的话,则只能唤醒其中一个线程

      而调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程

      当时的疑惑

      (1)既然notify或者notifyAll需要执行完synchronized块中的内容,那么他还有什么存在的价值的

        后来执行完之后,才发现要是没有这个方法,那么synchronized块执行完之后,waitthread还是在等待状态,无法被唤醒。

      (2)wait被notify唤醒之后,是接着执行,所以console打印5,并不是从头执行(如果在实例代码的wail()后加flag=true结果是截然不同,由于notirythread进程执行完,waitthread进程重新执行wait方法,此时会一直陷入wait,无其他进程唤醒此进程)

    二、wait()/wait(long)和sleep(long)方法的区别

      将示例代码中的lock.wait()改为Thread.sleep(1000),console打印

    WaitThread flag is true. wait@ 21:29:49
    此处继续执行WaitThread
    WaitThread flag is true. wait@ 21:29:50
    此处继续执行WaitThread
    WaitThread flag is true. wait@ 21:29:51
    此处继续执行WaitThread
    WaitThread flag is true. wait@ 21:29:52
    此处继续执行WaitThread

      由此说明sleep并没有释放锁。

      区别:

        1、Sleep(long)是Thread的方法,而wait()/wait(long)是Object的方法

        2、Sleep(long)可以放在sychnoized块内也可以不放在里面,但是wait()/wait(long)必须放在语句块内

        3、Sleep(long)不释放锁,只是让当前线程暂停一段时间,而wait()/wait(long)是释放锁

        4、wait()将当前线程放到阻塞队列,只有调用notify()/notifyAll()方法后,才将其从阻塞队列中移动到就绪队列,等待被CPU调度,而wait(long)方法执行后就是放到阻塞队列,等待时间到期或者被wait()/wait(long)唤醒后就可以放到就绪队列被CPU调度

      目前还有一个疑惑,就是线程中的run方法有两个同样的synchroized(lock),是不是跟一个synchroized(lock)效果是一样的,目前就运行结果来看是这样子的!

  • 相关阅读:
    多任务顺序执行解决方案
    数码摄影学习总结
    ASP.NET Core与RESTful API 开发实战(二)
    通过三点求圆心程序(二维和三维两种方式),代码为ABB机器人程序,其他语言也适用
    ABB机器人选项611-1 Path Recovery使用记录
    C#刷新chart控件方法及task的启停功能记录
    ABB机器人输送链跟踪问题记录
    有关C#跨线程操作控件的委托方法
    c#get、set属性及类的继承
    正则表达式学习记录
  • 原文地址:https://www.cnblogs.com/sxkgeek/p/9061798.html
Copyright © 2020-2023  润新知