Java面试中高频问题:深入理解“可重复读”与“不可重复读”

一、什么是“可重复读”?
在Java并发编程中,“可重复读”是指在进行读取操作时,无论读取多少次,都能获取到相同的值。这个概念在多线程环境下尤为重要,因为多线程可能会导致数据的读取不一致。
二、什么是“不可重复读”?
与“可重复读”相对的是“不可重复读”,它是指在读取操作中,由于其他线程的修改,导致同一数据在多次读取中得到的值不同。
三、如何理解“可重复读”与“不可重复读”?
1. 可重复读的原理
在Java中,为了实现“可重复读”,通常使用乐观锁或悲观锁。乐观锁认为冲突的概率较小,在读取数据时不对数据进行锁定,只有在更新数据时才加锁。悲观锁则相反,认为冲突的概率较大,在读取数据时就会加锁,确保数据的读取和更新都是串行化的。
2. 不可重复读的原理
不可重复读产生的原因是多线程环境下,其他线程对共享数据的修改导致读取结果不一致。在Java中,这种现象通常是由于并发控制不当引起的。
四、案例分析
以下是一个简单的案例分析,用于说明“可重复读”与“不可重复读”在实际开发中的应用。
1. 可重复读案例
假设有两个线程A和B,分别从同一个数据库表中读取数据。在A线程读取数据的过程中,B线程并没有对数据进行修改,此时A线程的读取操作就是可重复的。
```java
public class RepeatableRead {
private static Object lock = new Object();
public static void main(String[] args) {
Thread A = new Thread(() -> {
synchronized (lock) {
// 读取数据
System.out.println("Thread A: " + getData());
}
});
Thread B = new Thread(() -> {
synchronized (lock) {
// 读取数据
System.out.println("Thread B: " + getData());
}
});
A.start();
B.start();
}
private static int getData() {
// 假设这是从数据库中读取的数据
return 10;
}
}
```
输出结果:Thread A: 10,Thread B: 10
2. 不可重复读案例
以下是一个不可重复读的案例,说明在多线程环境下,读取数据时可能出现不一致的情况。
```java
public class RepeatableRead {
private static Object lock = new Object();
public static void main(String[] args) {
Thread A = new Thread(() -> {
synchronized (lock) {
// 读取数据
System.out.println("Thread A: " + getData());
// 稍作延时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 再次读取数据
System.out.println("Thread A: " + getData());
}
});
Thread B = new Thread(() -> {
synchronized (lock) {
// 修改数据
setData(20);
}
});
A.start();
B.start();
}
private static int getData() {
// 假设这是从数据库中读取的数据
return 10;
}
private static void setData(int data) {
// 假设这是将数据写回数据库的操作
System.out.println("Data has been set to: " + data);
}
}
```
输出结果:Thread A: 10,Thread A: 20
五、总结
本文深入分析了Java并发编程中的“可重复读”与“不可重复读”的概念、原理和案例分析。在实际开发中,我们需要注意对并发控制的处理,避免因不当的并发操作导致数据不一致的问题。了解并掌握“可重复读”与“不可重复读”的相关知识,有助于提高代码质量和系统稳定性。






