美文网首页
线程学习->05wait、notify、yield、joi

线程学习->05wait、notify、yield、joi

作者: 冉桓彬 | 来源:发表于2017-10-12 20:11 被阅读14次

    一、参考文章:

    二、基础api:

    1. Object.wait();
    2. Object.notify();
    3. Thread.join();
    4. Thread.yield();
    5. Thread.sleep();
    

    三、wait与notify:

    3.1 demo(wait与notify):
    public static void waitNotifyTest() {
        final Object obj = new Object();
        new Thread(new Runnable() {
            @Override
            public void run() {
                LogUtils.log(getClass(), "1. 线程A正在尝试获取锁");
                synchronized (obj) {
                    try {
                        LogUtils.log(getClass(), "2. 线程A获取锁成功");
                        SystemClock.sleep(2000);
                        LogUtils.log(getClass(), "3. 线程A正在尝试挂起");
                        obj.wait();
                        LogUtils.log(getClass(), "4. 线程A挂起结束");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                LogUtils.log(getClass(), "5. 线程B正在尝试获取锁");
                synchronized (obj) {
                    LogUtils.log(getClass(), "6. 线程B获取锁成功");
                    SystemClock.sleep(2000);
                    LogUtils.log(getClass(), "7. 线程B正在尝试唤醒线程A");
                    obj.notify();
                    LogUtils.log(getClass(), "8. 线程B唤醒线程A结束");
                }
            }
        }).start();
    }
    

    打印结果如下:

    05-06 18:35:54.698 4020-4063/com.test V/AndroidTest: ->1. 线程A正在尝试获取锁
    05-06 18:35:54.698 4020-4063/com.test V/AndroidTest: ->2. 线程A获取锁成功
    05-06 18:35:54.698 4020-4064/com.test V/AndroidTest: ->5. 线程B正在尝试获取锁
    05-06 18:35:56.698 4020-4063/com.test V/AndroidTest: ->3. 线程A正在尝试挂起
    05-06 18:35:56.748 4020-4064/com.test V/AndroidTest: ->6. 线程B获取锁成功
    05-06 18:35:58.748 4020-4064/com.test V/AndroidTest: ->7. 线程B正在尝试唤醒线程A
    05-06 18:35:58.748 4020-4064/com.test V/AndroidTest: ->8. 线程B唤醒线程A结束
    05-06 18:35:58.748 4020-4063/com.test V/AndroidTest: ->4. 线程A挂起结束
    

    在没看占小狼博客之前我是懵逼的;

    3.2 关于demo有几个简短的结论:
    • 1、线程A调用obj.wait之后, 是一定释放了锁操作, 否则线程B会在synchronized(obj)处进入阻塞状态, 直到线程A退出synchronized(obj);
    • 2、obj.wait会挂起当前线程(Thread-1), obj.notify会唤醒调用obj.wait被挂起的线程;
    3.3 关于demo有几个疑问:
    • 1、线程A执行obj.wait之前, 线程B处于挂起状态, 线程A执行obj.wait操作, 线程B如何被唤醒?
    3.4 复制一段<深入理解java虚拟机>(P391):

      synchronized关键字经过编译之后, 会在同步块的前后分别形成monitorenter和monitorexit这两个字节码指令, 这两个字节码都需要一个reference类型的参数来指明要锁定和解锁的对象; 如果java程序中的synchronized明确指定了对象参数, 那就是这个对象的reference, 如果没有明确指定, 那就根据synchronized修饰的实例方法还是类方法, 去取对应的对象实例或Class对象来作为锁对象;
      在执行monitorenter指令时, 首先尝试获取对象的锁(monitor), 成功--锁的计数器加1, 失败--线程被挂起, 直到锁被释放;

    3.5 wait/notify:

    汇编搞不懂啊, 只记结论太伤了;
    总结一下就是:

    • 1、线程执行到synchronized时如果发现对象锁被其他线程持有, 就会挂起进入Blocking队列
    • 2、线程执行到obj.wait时, 会释放当前对象锁(这里也说明了为什么wait一定要在synchronized中进行, 因为synchronized会执行monitorenter指令, 执行该指令又会去尝试获取对象锁monitor), 然后挂起进入Wait队列, 并唤醒Blocking队列中的线程;
    • 3、Blocking队列中的线程被唤醒后尝试自旋获取锁;
    • 4、调用obj.notify时, 会唤醒Wait队列中的线程, Wait队列中的线程被唤醒之后, 通过自旋尝试获取锁, 成功继续向下执行, 失败被挂起;

    四、Thread.yield:

    • 1、java线程中的Thread.yield方法是线程让步的意思, 当一个线程使用了这个方法之后, 它就会把自己的CPU执行的时间让掉, 让自己活其它线程运行, 并不是单纯的让给其他线程;
    • 2、yield()让当前线程由"运行状态"进入到"就绪状态", 从而让其它具有相同优先级的等待线程获取执行权, 并不能保证在当前线程调用yield()之后, 其它具有相同优先级的线程就一定能获得执行权, 也有可能是当前线程又进入到"运行状态"继续运行;

    五、Thread.join:

    • 1、join内部调用了wait, 会让出锁, sleep会一直保持锁;

    相关文章

      网友评论

          本文标题:线程学习->05wait、notify、yield、joi

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