线程间通信(wait,notify,notifyAll,join)

线程间通信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区别?
相同点:

  1. 他们都是在多线程的环境下,都可以在程序的调用处阻塞指定的毫秒数,并返回。
  2. 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: 线程生命周期?

  1. NEW ( 没有start之前)
  2. RUNNABLE ( start之后 ,System.in)
  3. TIMED_WAITING (wait(1000) Thread.sleep(3000) t1.join(1000) lock.tryLock(10, TimeUnit.SECONDS))
  4. WAITING ( wait() t1.join() lock.lock();)
  5. BLOCKED (synchronized)
  6. TERMINATED ( run方法运行结束)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值