在Java并发编程中,volatile和synchronized是两个至关重要的关键字,它们帮助开发者解决多线程环境下的可见性、原子性和有序性问题。
一. 简介
1. Volatile
volatile是一个类型修饰符,用来确保对一个变量的读取和写入操作不会被编译器重排序,并且每次读取都直接从主内存中获取最新值,写入时也直接写入主内存,从而保证了多线程环境下的可见性。但需要注意的是,volatile不能保证复合操作(如count++)的原子性。
volatile关键字用于确保变量的可见性(visibility)和有序性(ordering),但不保证原子性(atomicity)。
2. Synchronized
synchronized关键字可以用于方法或代码块,它提供了一种锁机制,确保同一时间只有一个线程可以执行特定的代码段,从而达到线程互斥的效果。synchronized不仅保证了可见性,还确保了操作的原子性以及有序性,是更强大的并发控制工具。
二、用法
1. Volatile 的使用
public class VolatileExample {private volatile int count = 0;public void increment() {count++;}public int getCount() {return count;}
}
在这个例子中,虽然count使用了volatile,但increment()方法并不是线程安全的,因为count++不是原子操作。然而,getCount()能保证读取到最新的count值。
2 Synchronized 的使用
- 方法级别
public class SynchronizedExample {private int count = 0;public synchronized void increment() {count++;}public synchronized int getCount() {return count;}
}
- 代码块级别
public class SynchronizedBlockExample {private int count = 0;private Object lock = new Object();public void increment() {synchronized (lock) {count++;}}public int getCount() {synchronized (lock) {return count;}}
}
三、常用案例实例
- Volatile 示例:确保一个标志的可见性
public class VolatileExample { // 使用volatile关键字确保变量的可见性 private volatile boolean running = true; public void start() { new Thread(() -> { while (running) { // 模拟一些工作 System.out.println("Thread is running..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("Thread is stopping..."); }).start(); } public void stop() { running = false; // 当running被设置为false时,其他线程会立即看到这个改变 } public static void main(String[] args) { VolatileExample example = new VolatileExample(); example.start(); // 模拟一段时间后停止线程 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } example.stop(); // 设置running为false,期望线程停止 }
}
- Synchronized 解决并发修改问题
考虑一个经典的银行账户转账问题:
public class BankAccount {private double balance;public synchronized void deposit(double amount) {balance += amount;}public synchronized void withdraw(double amount) {if (amount <= balance) {balance -= amount;} else {System.out.println("Insufficient balance.");}}public synchronized double getBalance() {return balance;}
}
在这个例子中,通过synchronized关键字确保了存取款操作的原子性和一致性。