刚入门java并发编程,总是听到各种锁的介绍。所以以下总结了目前我所知道的各种锁以及他们的一些基本的功能介绍

独享锁/共享锁

独享锁是指该锁一次只能被一个线程所持有。 (ReentrantLock、 Synchronized)

共享锁是指该锁可被多个线程所持有。 (ReadWriteLock)

公平锁/非公平锁

公平锁是指多个线程严格按照申请锁的顺序来获取锁。

非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。但也有可能会造成饥饿现象,简单来说就是某个线程长期无法获取到锁。

可重入锁

可重入锁又名递归锁,是指同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁,可重入锁的一个好处是可一定程度避免死锁

public synchronized void call() {
System.out.println("call phone");
sendMessage();
}
public synchronized void sendMessage(){
System.out.println("send message");
}

如上面的代码,如果synchronized不是可重入锁的话,sendMessage就不会被当前线程执行,从而形成死锁。需要注意的是,可重入锁加锁和解锁的次数要相等。

乐观锁/悲观锁

乐观锁/悲观锁不是指具体类型的锁,而是看待并发的角度。

悲观锁认为存在很多并发更新操作,采取加锁操作,如果不加锁一定会有问题

乐观锁认为不存在很多的并发更新操作,不需要加锁。数据库中乐观锁的实现一般采用版本号,Java中可使用CAS实现乐观锁。

自旋锁/自适应自旋锁

如果持有锁的线程能够在很短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态(一直处于用户态,即active,被阻塞后进入内核调度状态Linux),只需要等一等(自旋),等持有锁的线程释放完锁后立即获取锁,这样就避免用户线程和内核的切换的消耗

但是线程自旋是需要消耗CPU的,说白了就是让CPU做无用功,如果一直获取不到锁,那线程也不能一直占有CPU自旋做无用功,所以需要设定一个自旋等待的最大时间,自旋锁默认的自旋次数值是10次,可以使用参数-XX:PreBlockSpin更改。

但每个线程的自旋次数都选为10次显然不合适,就引入了自适应自旋锁。

自适应自旋锁意味着自旋的时间不再固定,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。

如果持有锁的线程执行的时间超过自旋等待的最大时间仍没有释放锁,就会导致其他争用锁的线程在最大等待时间内还是获取不到锁,这时争用线程会停止自旋进入阻塞状态。但是如果锁竞争激烈,或者持有锁的线程需要长时间占用锁执行同步块,这时候就不适合使用自旋锁了,因为自旋锁在获取锁前一直都是占用CPU做无用功。

偏向锁/轻量级锁/重量级锁

偏向锁(Biased Lock):无实际竞争,且将来只有第一个申请锁的线程会使用锁。

轻量级锁(Lightweight Lock):无实际竞争,多个线程交替使用锁;允许短时间的锁竞争。如果存在锁竞争但不激烈,仍然可以用自旋锁优化。优化失败后再转化为重量级锁

重量级锁(Heavyweight Lock):有实际竞争,且锁竞争时间长。

上述这三种机制的切换是根据竞争激烈程度进行的, 在几乎无竞争的条件下, 会使用偏向锁, 在轻度竞争的条件下, 会由偏向锁升级为轻量级锁, 在重度竞争的情况下, 会升级到重量级锁。

java 访问共享文件夹 用户名密码错误 java共享锁有哪些_乐观锁