面试题答案
一键面试synchronized关键字的内存语义
- 锁的获取:当线程获取synchronized锁时,Java内存模型会把该线程对应的本地内存置为无效。这意味着线程必须从主内存中重新读取共享变量的值。
- 锁的释放:当线程释放synchronized锁时,Java内存模型会把该线程对应的本地内存中的共享变量刷新到主内存中。这样,其他线程在获取到锁后,就能看到最新的值。
对原子性的保证
- 原理:synchronized关键字通过对临界区代码进行加锁,同一时刻只有一个线程能够进入临界区执行代码。在锁未释放前,其他线程无法进入,从而保证了临界区内代码执行的原子性。
- 示例:
public class AtomicExample {
private int count = 0;
public synchronized void increment() {
count++;
}
}
在上述代码中,increment
方法被synchronized
修饰,多个线程调用该方法时,只有一个线程能执行count++
操作,不会出现并发问题,保证了count++
操作的原子性。
对可见性的保证
- 原理:如前面内存语义所述,线程释放锁时会将本地内存中的共享变量刷新到主内存,线程获取锁时会将本地内存置为无效并从主内存重新读取共享变量。这确保了一个线程对共享变量的修改,在其他线程获取锁后是可见的。
- 示例:
public class VisibilityExample {
private int num = 0;
public synchronized void update() {
num = 1;
}
public synchronized void read() {
System.out.println(num);
}
}
在上述代码中,当一个线程调用update
方法修改num
后释放锁,num
的值被刷新到主内存。其他线程调用read
方法获取锁时,从主内存读取到的就是最新的num
值,保证了可见性。
对有序性的保证
- 原理:synchronized关键字不仅保证了同一时刻只有一个线程能执行临界区代码,还禁止了指令重排序。在获取锁时,会将本地内存置为无效,强制从主内存读取共享变量;在释放锁时,会将本地内存中的共享变量刷新到主内存。这一系列操作使得临界区内的代码按照顺序执行,不会发生指令重排序。
- 示例:
public class OrderingExample {
private int a = 0;
private int b = 0;
public synchronized void write() {
a = 1;
b = 2;
}
public synchronized void read() {
System.out.println(b);
System.out.println(a);
}
}
在上述代码中,write
方法内的a = 1
和b = 2
不会发生指令重排序,read
方法内的打印操作也会按照顺序执行,从而保证了有序性。