偏向锁 轻量级锁 重量级锁
1.5以前 synchronized通过操作系统的mutex lock实现,称为重量级锁
1.6做了优化,引入了偏向锁和轻量级锁的概念
- 偏向锁:认为没有线程会跟他抢资源,加锁解锁不会有额外的消耗。
- 轻量级锁:认为竞争很小且其他线程会很快释放资源,加锁失败会自旋等待。
对象刚new出来的时候,是可偏向的。第一个线程访问它的时候,修改对象头称为偏向锁,并修改threadId为自己。下一次再进来,比较threadId,如果是自己,就不需要做任何操作。
第二个线程进来,看到这个对象的偏向状态,表明对象存在竞争。如果之前那线程已经挂了或者那个线程没在用这个对象,就设置对象偏向自己。否则就偏向锁升级成轻量级锁
如果这时候第三个线程进来,也要访问该对象或者第二个线程获取锁失败后自旋了很长时间还没得到锁,轻量级锁就升级成重量级锁。
自旋锁
线程阻塞唤醒需要做内核态和用户态的切换,消耗比较大。如果持有锁的线程能在短时间内释放锁资源,竞争锁的线程就等一等(自旋,循环获取锁)。
Java里面可以通过cas实现。
cas
V(需要读写的内存位置) 、 A(进行比较的值) 、 B(期望写入的值)
cas操作含义:认为V的值应该是A,如果是,那么将V设置成B,否则不修改。返回V实际值是多少。
cas是操作系统实现的,保证原子性。
可重入锁
同一个线程能多次获得这个锁。synchronized、ReentrantLock都是可重入的
公平和非公平
公平:按发出请求的顺序获得锁
非公平:线程请求非公平锁时,如果发出请求的同时该锁状态变成可用,那么这个线程将跳过队列中所有的等待线程并获得这个锁。
ReentrantLock提供了这两种公平性的选择。默认是非公平的,性能比公平的高。因为公平锁需要做更多的线程挂起唤醒操作。
乐观锁与悲观锁
- 乐观锁:去拿数据时,认为别人不会去修改,所以不会上锁,更新的时候去判断下有没有修改。原子变量、偏向锁、轻量级锁这些都是乐观锁。
- 悲观锁:每次拿数据的时候都会上锁。synchronized膨胀成重量级锁就是个悲观锁。