java面试题-Java中的锁是什么?
在并发编程中,处理多个线程对共享变量的访问是一项重要的任务。为了确保数据的一致性,Java提供了多种锁机制。本教程将深入探讨Java中的锁,包括传统的synchronized
关键字和并发工具包中引入的更丰富的Lock
。
1. 锁的基本概念
1.1 synchronized关键字
在JDK 1.5之前,我们主要使用synchronized
关键字来处理锁。通过synchronized
,我们可以保护一段代码块,确保同一时刻只有一个线程可以执行该代码块。
public class SynchronizedExample {
private static int count = 0;
public synchronized void increment() {
count++;
}
}
1.2 java.util.concurrent.locks.Lock
JDK 1.5引入了更灵活的锁机制,通过java.util.concurrent.locks.Lock
接口及其实现类,我们可以更好地控制锁的行为。
1.2.1 可重入锁
可重入锁允许同一线程在外层方法获取锁的同时,进入内层方法自动获取锁。常见的可重入锁实现是ReentrantLock
。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private Lock lock = new ReentrantLock();
public void performTask() {
lock.lock();
try {
// 一些需要保护的代码
} finally {
lock.unlock();
}
}
}
1.2.2 公平锁与非公平锁
公平锁表示多个线程按照申请锁的顺序来获取锁,而非公平锁允许线程按照一定策略获取锁,可能导致后申请的线程先获得锁。在ReentrantLock
中,可以通过构造函数指定锁的公平性。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class FairnessExample {
private Lock fairLock = new ReentrantLock(true); // 公平锁
private Lock nonFairLock = new ReentrantLock(); // 非公平锁
}
1.2.3 独享锁与共享锁
独享锁表示一次只能被一个线程持有,而共享锁可以被多个线程同时持有。ReentrantLock
是独享锁的例子,而ReentrantReadWriteLock
提供了读写锁,读锁是共享锁,写锁是独享锁。
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample {
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private Lock readLock = readWriteLock.readLock(); // 共享锁
private Lock writeLock = readWriteLock.writeLock(); // 独享锁
}
1.2.4 悲观锁与乐观锁
悲观锁一律对代码块进行加锁,例如synchronized
和ReentrantLock
。乐观锁默认不加锁,通常采用CAS(比较与交换)算法不断尝试更新。悲观锁适合写操作较多的场景,而乐观锁适合读操作较多的场景。
1.3 锁的粒度
锁的粒度可以分为粗粒度锁和细粒度锁。粗粒度锁是对执行代码块进行整体加锁,而细粒度锁是锁住尽可能小的代码块。在java.util.concurrent.ConcurrentHashMap
中的分段锁就是一种细粒度锁的实现。
2. 锁的升级机制
2.1 偏向锁、轻量级锁和重量级锁
JDK 1.5之后,引入了锁的升级机制,包括偏向锁、轻量级锁和重量级锁。偏向锁是通过synchronized
加锁后,同一线程一直访问时获取的锁。当有其他线程访问时,升级为轻量级锁,其他线程通过自旋尝试获取锁。如果自旋一定次数仍未获取到锁,就升级为重量级锁,此时线程会进入阻塞状态。
public class LockUpgradeExample {
private static int sharedResource = 0;
public synchronized void synchronizedMethod() {
// 偏向锁
sharedResource++;
}
}
2.2 自旋锁
自旋锁是一种尝试获取锁的线程不会立即阻塞,而是采用循环的方式不断尝试获取锁的机制。这样的好处是减少线程上下文切换的消耗,但也可能导致自旋占用CPU资源。
3. 总结
本教程深入介绍了Java中的锁机制,包括传统的synchronized
关键字和更灵活的Lock
接口。我们讨论了不同维度的锁分类,锁的粒度,以及锁的升级机制。了解这些概念对于写出高效、可靠的多线程程序至关重要。
- 本文标签: Java 面试题
- 本文链接: https://www.jietongc.com/article/78
- 版权声明: 本文由大熊科技原创发布,转载请遵循《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权