你知道 Java 中有哪些锁吗?
- 公平锁 / 非公平锁:公平锁值按照线程申请锁的顺序获得锁。非公平锁不按照线程省钱锁的顺序获得锁。对于 ReentrantLock,默认是非公平锁。synchronized 也是一种非公平锁。非公平锁的性能更高。
- 可重入锁:线程外部代码获得锁时,内层代码可以直接获得锁。synchronized 和 ReentrantLock 都是可重入锁。可重入锁是为了防止死锁。
- 独享锁 / 共享锁:独享锁一个锁只能被一个线程占有。共享锁一个锁可以被多个线程占有。synchronized 和 ReentrantLock 都是独享锁。ReadWriteLock 读锁是共享锁,写锁是独享锁。
- 互斥锁 / 读写锁:是广义的独享锁 / 共享锁的具体实现。
- 悲观锁 / 乐观锁:悲观锁和乐观锁不是具体的某一种锁。而是一种锁的思想。悲观锁认为线程安全问题一定会发生,所以通过加锁保证线程安全。而乐观锁认为线程安全问题不一定会发生,所以先不加锁,而是通过版本号的形式,如果版本号发生变化,说明有别的线程修改过,放弃本次操作。悲观锁适合写多读少的情景,乐观锁适合读多写少的情景。
- 分段锁:分段锁是一种锁的设计,不是具体的某一种锁。如 ConcurrentHashMap 内部就是采用分段锁,从而实现高效的并发性能。
- 偏向锁 / 轻量级锁 / 重量级锁:偏向锁用于锁竞争不发生的情况。一个线程获取了某个偏向锁,下次来的时候可以直接获得锁。而此时如果发生了锁竞争,就会升级成轻量级锁。当别的线程获取不到锁时,会陷入自旋等待尝试重新获取锁。避免陷入阻塞等待从而降低性能。但是自旋的次数有上限。超过上限后,就会升级成重量级锁。线程获取锁失败会陷入阻塞状态。降低性能。
- 自旋锁:自旋锁指的是当一个线程获取锁失败时,不会陷入阻塞状态,而是会自旋重试去获取锁。好处是可以避免线程上下文切换带来的开销。坏处是会一直循环消耗 CPU 资源。
说一说你对 synchronized 的理解?
-
作用
synchronized 是 Java 中的一个关键字,用于实现同步和线程安全。 -
本质
synchronized 的本质是获取某个对象的监视器锁(monitor lock)。线程要进入同步代码块,必须先获得指定的锁。锁是属于对象的,而不是属于代码的。在 JVM 层面,是在一段指令的前后加了 monitorenter 和 monitorexit 指令来获取和释放锁。 -
解释作用
当某个方法或代码块被 synchronized 修饰后,它将成为一个临界区。同一时刻只有一个线程能执行这段代码。其他线程必须等待这个线程执行完毕退出临界区后才能进入。确保多个线程访问共享资源时不会产生冲突。 -
锁定方法和代码块
synchronized 可以修饰方法或代码块。当修饰方法时,整个方法被锁定。当修饰代码块时,只有代码块被锁定。这样做的好处是可以减少锁的范围。 -
不同的锁对应不同的对象
当修饰方法时,锁定的是 this 实例对象。当修饰代码块时,需要显示地指定锁定的对象。当修饰的是静态方法时,锁定是整个类对象。 -
synchronized 锁的优化
synchronized 锁在 Java 1.6 之后做了一些优化。引入了偏向锁、轻量级锁和重量级锁。当一个锁对象第一次被某个线程进入时,这个时候由于没有发送锁竞争,此时是偏向锁,线程可以直接获得锁对象。当出现别的线程也要竞争这个锁时,就会升级成轻量级锁。未获取到锁的线程会自旋重试。当自旋次数超过 10 次时,就会升级成重量级锁。未获取到锁的线程会陷入阻塞。

被折叠的 条评论
为什么被折叠?



