锁涉及的几个重要概念
死锁
线程之间相互等着对方释放资源,而自己的资源又不释放给别人,这种情况就是死锁。所以,只要其中一线程释放了资源,死锁就会被解除。
重入锁
重入锁指的是,一个线程在拥有了当前资源的锁之后,可以再次拿到该锁而不被阻塞。在后面会讲到synchronized的重入锁原理。
自旋锁
自旋锁指的是,线程在没有获得锁时,不是被直接挂起,而是执行一个空循环(自旋)。默认是循环10次。
自旋锁的目的也就是为了减少线程被挂起的几率,因为线程的挂起和唤醒也都是耗资源的操作。
如果锁被另一个线程占用的时间比较长,即使自旋了之后当前线程还是会被挂起,空循环就会变成浪费系统资源的操作,反而降低了整体性能。所以,自旋锁是不适应锁占用时间长的并发情况的。
自适应自旋锁
自适应自旋锁是对自锁锁的一种优化。当一个线程自旋后成功获得了锁,那么下次自旋的次数就会增加。因为虚拟机认为,既然上次自旋期间成功拿到了锁,那么后面的自旋会有很大几率拿到锁。相反,如果对于某个锁,很少有自旋能够成功获得的,那么后面就会减少自旋次数,甚至省略掉自旋过程,以免浪费处理器资源。
这种锁是默认开启的。
锁消除
锁消除指的是,在编译期间利用“逃逸分析技术”分析出那些不存在竞争却加了锁的代码的锁失效。这样就减少了锁的请求与释放操作,因为锁的请求与释放都会消耗系统资源。
锁偏向
偏向锁指的是,当第一个线程请求时,会判断锁的对象头里的ThreadId字段的值,如果为空,则让该线程持有偏向锁,并将ThreadId的值置为当前线程ID。当前线程再次进入时,如果线程ID与ThreadId的值相等,则该线程就不会再重复获取锁了。因为锁的请求与释放是要消耗系统资源的。
如果有其他线程也来请求该锁,则偏向锁就会撤销,然后升级为轻量级锁。如果锁的竞争十分激烈,则轻量级锁又会升级为重量级锁。
锁粗化
锁粗化指的是,在编译期间将相邻的同步代码块合并成一个大同步块。这样做可以减少反复申请和释放同一个锁对象导致的系统开销。锁粗化也是默认开启的。
synchronized也属于可重入锁。
private ReentrantLock lock = new ReentrantLock();
public void testLock() {
// 获取锁
lock.lock();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("test ReentrantLock ");
// 释放锁
lock.unlock();
}
同步操作的实现,需要给对象关联一个互斥体,这个互斥体就可以叫做锁
锁的作用是,保证同一竞争资源在同一时刻只会有一个线程占有
Java中锁的实现方式有两种:synchronized关键字和并发包中的锁类
锁的优化策略有:锁消除、锁偏向、自适应自旋锁、锁粗化
尽量不要在循环内使用锁,以减少资源消耗