线程间通信wait()你真正的了解?
https://blog.csdn.net/starryninglong/article/details/81144894
问1:wait()方法一定要使用sycronized进行同步吗?不用sycronized修饰会有什么问题?
测试:
public class Test01 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
wait();
}
});
t.start();
Thread.sleep(50000L);
}
}
结果:
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:502) at com.paat.saas.crm.auth.Test01$1.run(Test01.java:13) at java.lang.Thread.run(Thread.java:745)
答案: wait()一定要使用sycronized进行同步,否则会报“java.lang.IllegalMonitorStateException”异常。
这是因为wait方法会释放对象锁,而此时因为没有用sycronized同步,就没有锁,就会报异常
问2:wait()方法 使用了synchronized同步,就一定不会报java.lang.IllegalMonitorStateException 异常吗?
测试:
public class Pic{
}
public class Test01 {
public static void main(String[] args) throws InterruptedException {
Pic pic = new Pic();
Thread t = new Thread(new Runnable() {
@Override
public void run() {
synchronized(pic){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
Thread.sleep(50000L);
}
}
结果:Exception in thread "Thread-0" java.lang.IllegalMonitorStateException at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:502) at com.paat.saas.crm.auth.Test01$1.run(Test01.java:15) at java.lang.Thread.run(Thread.java:745) Process finished with exit code 0
为什么用了synchronized同步,怎么还报ava.lang.IllegalMonitorStateException异常,奇怪了?
解释:原因在于synchronized 获得的是 pic的对象锁(synchronized 使得pic对象成为一把对象锁),但是wait() 也就是this.wait(),等待后释放的确是当前线程对象,但是当前线程并没有成为一个对象锁,所以释放的时候报错,找不到要释放的锁。
ok 知道原因了,让我们修改代码,测试一把。
public class Test01 {
public static void main(String[] args) throws InterruptedException {
Pic pic = new Pic();
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("获得pic对象锁");
//第一种修改方法
/* synchronized(pic){
try {
System.out.println("释放pic对象锁,等待");
pic.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}*/
//第二种修改方法
synchronized(this){
try {
System.out.println("释放pic对象锁,等待");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
//主线程等到50s ,防止主线程退出
Thread.sleep(50000L);
}
结果:
获得pic对象锁
释放pic对象锁,等待
总结:
t.wait(),使得调用当前线程阻塞,然后释放,对象t上的对象锁。
网上有很多,锁t.wait() 方法必须要要在sycronized中使用,但是并没有说sycronized必须让t成为对象锁。若是当前线程对象t就是this
问3: t.join 是怎么阻塞当前线程,是怎么让当前线程必须要在t线程运行完后,在运行当前线程的?
测试(未使用join):
public static void main(String[] args) throws InterruptedException {
Pic pic = new Pic();
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("获得pic对象锁");
synchronized(this){
System.out.println("获得pic对象锁成功");
}
}
});
t.start();
System.out.println("主线程运行");
//主线程等到50s ,防止主线程退出
Thread.sleep(50000L);
}
结果:
主线程运行
获得pic对象锁
获得pic对象锁成功
测试(使用join):
public static void main(String[] args) throws InterruptedException {
Pic pic = new Pic();
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("获得pic对象锁");
synchronized(this){
System.out.println("获得pic对象锁成功");
}
}
});
t.start();
t.join();
System.out.println("主线程运行");
//主线程等到50s ,防止主线程退出
Thread.sleep(50000L);
}
结果:
获得pic对象锁
获得pic对象锁成功
主线程运行
join原理:当前线程运行t.join后,会争抢t对象锁,然后调用wait释放t对象锁并阻塞当前线程.只有t线程运行结束后,会调用notifyAll()—这个调用是jvm自动调用,通知获得T对象锁的等到线程。
直接上join源码证明上边解释:
public final void join() throws InterruptedException {
join(0);
}
//synchronized 修饰方法,获得对象锁
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) {
//循环检查t线程是否活着,活着就释放t对象锁,然后等待
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
问4: wait(long timeout) 和wait() 什么区别?
wait(long timeout) 超时等待,唯一区别是在给定时间,自动唤醒等待。
问5: wait(long timeout, int nanos) 中nanos作用?
源码:
作者:知乎用户
链接:https://www.zhihu.com/question/41808470/answer/137420141
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
public final void wait(long timeout, int nanos) throws nterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
//nanos 单位为纳秒, 1毫秒 = 1000 微秒 = 1000 000 纳秒
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
// nanos 大于 500000 即半毫秒 就timout 加1毫秒
// 特殊情况下: 如果timeout为0且nanos大于0,则timout加1毫秒
if (nanos >= 500000 || (nanos != 0 && timeout == 0)) {
timeout++;
}
wait(timeout);
}
解释:
要点在于知道 timeout的单位为毫秒, 参数nanos 的单位为纳秒, 1毫秒 = 1000 微秒 = 1000 000 纳秒处理时,由于纳秒级时间太短(我猜测), 所以对参数nanos 其采取了近似处理,即大于半毫秒的加1毫秒,小于1毫秒则舍弃(特殊情况下,参数timeout为0时,参数nanos大于0时,也算为1毫秒)其主要作用应该在能更精确控制等待时间(尤其在高并发时,毫秒的时间节省也是很值得的)
来源:https://www.zhihu.com/question/41808470/answer/137420141
问5: wait和sleep区别?
相同点:
- 他们都是在多线程的环境下,都可以在程序的调用处阻塞指定的毫秒数,并返回。
- wait()和sleep()都可以通过interrupt()方法 打断线程的暂停状态 ,从而使线程立刻抛出InterruptedException。
如果线程A希望立即结束线程B,则可以对线程B对应的Thread实例调用interrupt方法。如果此刻线程B正在wait/sleep /join,则线程B会立刻抛出InterruptedException,在catch() {} 中直接return即可安全地结束线程。
需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用 interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到 wait()/sleep()/join()后,就会立刻抛出InterruptedException 。
不同点:
1.每个对象都有一个锁来控制同步访问。Synchronized关键字可以和对象的锁交互,来实现线程的同步。
sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
2.wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
3.sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
4. sleep()睡眠时,保持对象锁,仍然占有该锁;而wait()睡眠时,释放对象锁。
问6: 线程生命周期?
- NEW ( 没有start之前)
- RUNNABLE ( start之后 ,System.in)
- TIMED_WAITING (wait(1000) Thread.sleep(3000) t1.join(1000) lock.tryLock(10, TimeUnit.SECONDS))
- WAITING ( wait() t1.join() lock.lock();)
- BLOCKED (synchronized)
- TERMINATED ( run方法运行结束)