synchronized锁升级的过程
注意上图两条主线:匿名偏向锁启动和没启动两种方式。
对象头信息
yield()、wait()、sleep()、join()四个方法的介绍
sleep():
是Thread类的静态方法,当前线程将睡眠n毫秒,线程进入阻塞状态。当睡眠时间到了,会解除阻塞,进入可运行状态,等待cpu的到来。睡眠不释放锁(如果有的话)。
wait():
是Object的方法,必须与synchronized关键字一起使用,线程进入阻塞状态,当notify或者notifyall被调用后,会解除阻塞。但是,只有重新占用互斥锁之后才会进入可运行状态。睡眠时,会释放互斥锁。
注意:因为wait()方法使用在synchronized修饰的范围内,所以:无论锁现在是何状态,只要调用wait()方法都会升级为重量级锁。
yield():
yield方法可以暂停当前正在执行的线程对象,让其它有相同优先级的线程执行。它是一个静态方法而且只保证当前线程放弃cpu执行权而不能保证使其它线程一定能占用cpu,执行yield()的线程有可能在进入到就绪状态后马上又被执行。
join():
A线程调用join代表A线程要插在当前线程的前面执行;通过join()方法的源码可知,它的底层还是wait()方法,一般join() 适用于需要控制线程执行顺序的场景中。
sleep()和wait()方法的异同:
- sleep 方法没有释放锁,而 wait 方法释放了锁 。
- sleep 通常被用于暂停执行;Wait 通常被用于线程间交互/通信
- sleep() 方法执行完成后,线程会自动苏醒。或者可以使用 wait(long timeout)超时后线程会自动苏醒。wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify() 或者 notifyAll() 方法
- sleep 方法和 wait 方法都可以用来放弃 cpu 一定的时间,不同点在于如果线程持有某个对象的监视器,sleep 方法不会放弃这个对象的监视器,wait方法会放弃这个对象的监视器
为什么wait()的时候必须释放锁?
当线程A进入synchronized(obj1)中之后,也就是对obj1上了 锁。此时,调用wait()进入阻塞状态,一直不能退出synchronized代码块;那么,线程B永远无法进入synchronized(obj1)
同步块里,永远没有机会调用notify(),岂不是死锁了!这就涉及一个关键的问题:在wait()的内部,会先释放锁obj1,然后进入阻塞状态,之后,它被另外一个线程用notify()唤醒, 去重新拿锁!其次,wait()调用完成后,执行后面的业务逻辑代码,然后退出synchronized同步块,再次释放锁。
Thread.sleep(0)的作用是什么?
由于Java采用抢占式的线程调度算法,因此可能会出现某条线程常常获取到cpu控制权的情况,为了让某些优先级比较低的线程也能获取到 cpu 控制权,可以使用Thread.sleep(0)手动触发一次操作系统分配时间片的操作,这也是平衡cpu控制权的一种操作。