一、参考文章:
二、基础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会一直保持锁;
网友评论