Java多线程:volatile和synchronized关键字
volatile 和 synchronized 是 Java 中用于处理多线程并发问题的关键字
1. volatile 关键字
作用
-
可见性:
volatile确保变量的修改对所有线程立即可见。当一个线程修改了volatile变量,其他线程能立即看到最新的值。 -
禁止指令重排序:
volatile会禁止 JVM 对变量的读写操作进行重排序,保证操作顺序与代码顺序一致。
特点
-
保证可见性:变量的修改对所有线程立即可见。
-
禁止指令重排序:确保变量的读写操作顺序与代码顺序一致。
-
不保证原子性:不能用于复合操作(如
i++)。
适用场景
-
状态标志:用于标记线程的状态(如启动、停止)。
-
一次性发布:确保对象的构造和初始化完成后才对其他线程可见。
-
独立变量的可见性:当变量的写操作不依赖于当前值,且没有复合操作时。
示例
状态标志
public class VolatileExample {
private volatile boolean running = true; // 状态标志
public void stop() {
running = false; // 修改 volatile 变量
}
public void run() {
while (running) {
// 执行任务
System.out.println("Running...");
}
System.out.println("Stopped.");
}
public static void main(String[] args) throws InterruptedException {
VolatileExample example = new VolatileExample();
Thread thread = new Thread(example::run);
thread.start();
Thread.sleep(1000); // 主线程休眠 1 秒
example.stop(); // 修改 running 标志,停止线程
}
}
一次性发布
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
// 私有构造函数
}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) { // 加锁
if (instance == null) { // 第二次检查
instance = new Singleton(); // 创建实例
}
}
}
return instance;
}
}
2. synchronized 关键字
作用
-
原子性:
synchronized保证代码块或方法的原子性,同一时间只有一个线程可以执行该代码块或方法。 -
可见性:
synchronized确保线程释放锁时,所有修改对其他线程可见。 -
互斥性:
synchronized保证同一时间只有一个线程可以访问被保护的代码块或方法。
特点
-
保证原子性:同一时间只有一个线程可以执行被保护的代码块或方法。
-
保证可见性:线程释放锁时,所有修改对其他线程可见。
-
提供互斥性:同一时间只有一个线程可以访问被保护的代码。
适用场景
-
复合操作:需要保证多个操作的原子性(如
i++)。 -
共享资源访问:多个线程需要访问和修改共享资源时。
-
线程同步:需要控制线程的执行顺序。
使用方式
-
修饰实例方法:锁是当前实例对象。
public synchronized void method() { // 线程安全的代码 } -
修饰静态方法:锁是当前类的
Class对象。public static synchronized void staticMethod() { // 线程安全的代码 } -
修饰代码块:锁是指定对象。
public void blockMethod() { synchronized (this) { // 锁是当前实例对象 // 线程安全的代码 } }
示例
复合操作
public class Counter {
private int count = 0;
public synchronized void increment() {
count++; // 原子操作
}
public synchronized int getCount() {
return count;
}
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("Final Count: " + counter.getCount()); // 输出 2000
}
}
共享资源访问
public class SharedResource {
private int value = 0;
public synchronized void setValue(int value) {
this.value = value;
}
public synchronized int getValue() {
return value;
}
public static void main(String[] args) {
SharedResource resource = new SharedResource();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
resource.setValue(resource.getValue() + 1);
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final Value: " + resource.getValue()); // 输出 2000
}
}
线程同步
public class SynchronizedExample {
private boolean isReady = false;
public synchronized void waitUntilReady() throws InterruptedException {
while (!isReady) {
wait(); // 等待其他线程通知
}
}
public synchronized void setReady() {
isReady = true;
notifyAll(); // 通知所有等待的线程
}
public static void main(String[] args) {
SynchronizedExample example = new SynchronizedExample();
Runnable task1 = () -> {
try {
example.waitUntilReady();
System.out.println("Task 1 is running.");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Runnable task2 = () -> {
try {
Thread.sleep(1000); // 模拟耗时操作
example.setReady(); // 设置 ready 状态
System.out.println("Task 2 set ready.");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread1 = new Thread(task1);
Thread thread2 = new Thread(task2);
thread1.start();
thread2.start();
}
}
volatile 和 synchronized 的区别
| 特性 | volatile |
synchronized |
|---|---|---|
| 原子性 | 不保证复合操作的原子性 | 保证代码块或方法的原子性 |
| 可见性 | 保证变量的可见性 | 保证代码块或方法内的可见性 |
| 互斥性 | 不提供互斥性 | 提供互斥性 |
| 性能 | 性能开销较小 | 性能开销较大 |
| 适用场景 | 状态标志、一次性发布等简单场景 | 复合操作、共享资源修改等复杂场景 |
总结
-
使用
volatile时,适用于简单的可见性需求,但不适合复合操作。 -
使用
synchronized时,适用于需要原子性和互斥性的场景,但要注意性能开销。 -
在实际开发中,可以根据需求选择合适的关键字,或结合使用(如
volatile修饰状态标志,synchronized保护复合操作)。
更多推荐


所有评论(0)