美文网首页
线程学习->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