在 Java 多线程编程中,同步是确保多个线程在访问共享资源时的正确性和一致性的重要机制。Java 提供了多种方式来实现线程同步,每种方式都有其特点和适用场景。以下是一些常见的同步方式及其特点和应用场景,以及简单的例子来说明它们的用法。
1. `synchronized` 关键字
特点
- 自动锁定和释放锁。
- 支持重入。
- 非公平锁。
- 只能用于实例方法和静态方法。
应用场景
- 简单的同步需求,如同步方法或代码块。
例子
public class SynchronizedExample {
private int count = 0;
public synchronized void increment() {
count++;
}
public static void main(String[] args) {
SynchronizedExample example = new SynchronizedExample();
Thread t1 = new Thread(example::increment);
Thread t2 = new Thread(example::increment);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Count is: " + example.count);
}
}
2. `ReentrantLock` 类
特点
- 需要手动锁定和解锁。
- 支持公平锁和非公平锁。
- 提供了比 `synchronized` 更灵活的锁定机制,如尝试锁定和条件等待。
应用场景
- 需要更灵活锁定策略的场景,如尝试锁定或条件锁定。
例子
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private Lock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock(); // 获取锁
try {
count++;
} finally {
lock.unlock(); // 释放锁
}
}
public static void main(String[] args) {
ReentrantLockExample example = new ReentrantLockExample();
Thread t1 = new Thread(example::increment);
Thread t2 = new Thread(example::increment);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Count is: " + example.count);
}
}
3. `synchronized` 块
特点
- 手动锁定和解锁。
- 支持重入。
- 非公平锁。
- 可以用于任何代码块。
应用场景
- 当只需要同步代码块而不是整个方法时。
例子
public class SynchronizedBlockExample {
private int count = 0;
private final Object lock = new Object();
public void increment() {
synchronized (lock) {
count++;
}
}
public static void main(String[] args) {
SynchronizedBlockExample example = new SynchronizedBlockExample();
Thread t1 = new Thread(example::increment);
Thread t2 = new Thread(example::increment);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Count is: " + example.count);
}
}
4. `volatile` 关键字
特点
- 确保变量的可见性。
- 禁止指令重排。
- 只能用于变量,不能用于方法或代码块。
应用场景
- 当多个线程只需要读写单个共享变量时。
例子
public class VolatileExample {
private volatile boolean flag= true;
public void run() {
while (flag) {
System.out.println("Thread " + Thread.currentThread().getId() + " is running");
}
}
public static void main(String[] args) throws InterruptedException {
VolatileExample example = new VolatileExample();
Thread t1 = new Thread(example::run);
Thread t2 = new Thread(example::run);
t1.start();
t2.start();
// 主线程休眠一段时间
Thread.sleep(500);
// 更改 flag 的值,使 t1 线程停止
example.flag = false;
}
}
5. `AtomicInteger` 类
特点
- 提供原子操作的整数类。
- 支持自增、自减等原子操作。
- 非公平锁。
应用场景
- 当需要对整数进行原子操作时,如计数器。
例子
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerExample {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}
public static void main(String[] args) {
AtomicIntegerExample example = new AtomicIntegerExample();
Thread t1 = new Thread(example::increment);
Thread t2 = new Thread(example::increment);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Count is: " + example.count);
}
}
6. `CountDownLatch`、`CyclicBarrier` 和 `Semaphore` 类
特点
- 这些类提供了线程间的协调和同步机制。
- `CountDownLatch` 允许一个或多个线程等待直到一组操作执行完毕。
- `CyclicBarrier` 允许一组线程相互等待,直到所有线程都达到某个屏障点。
- `Semaphore` 用于限制可以同时访问某个资源的最大线程数。
应用场景
- 当需要线程间的协调和同步时,如多个线程完成任务后再执行下一步操作。
例子(`CountDownLatch`)
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
private CountDownLatch latch = new CountDownLatch(2);
public void performTask() {
try {
latch.await(); // 等待直到计数器下降到 0
System.out.println("All tasks are completed");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
CountDownLatchExample example = new CountDownLatchExample();
Thread t1 = new Thread(example::performTask);
Thread t2 = new Thread(example::performTask);
t1.start();
t2.start();
// 主线程休眠一段时间
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 计数器减到 0,使等待的线程继续执行
example.latch.countDown();
example.latch.countDown();
}
}
总结
Java 提供了多种同步机制,包括 `synchronized` 关键字、`ReentrantLock`、`synchronized` 块、`volatile` 关键字、`AtomicInteger` 类以及 `CountDownLatch`、`CyclicBarrier` 和 `Semaphore` 类。每种机制都有其特点和适用场景。`synchronized` 关键字和 `ReentrantLock` 用于锁定代码块或方法,确保多线程对共享资源的独占访问。`volatile` 关键字用于确保变量的可见性和有序性。`AtomicInteger` 类提供了一种原子性的整数操作,适合于计数器等场景。而 `CountDownLatch`、`CyclicBarrier` 和 `Semaphore` 类则用于线程间的协调和同步,它们通过不同的机制来控制线程的执行流程。
在实际应用中,选择合适的同步机制取决于具体的需求和场景。例如,如果你需要一个简单的锁机制,`synchronized` 关键字或 `ReentrantLock` 都是不错的选择。如果你需要对单个变量进行原子操作,`volatile` 关键字或 `AtomicInteger` 类会更加适合。而当你需要线程间的协调,例如等待所有线程完成任务后再继续执行时,`CountDownLatch`、`CyclicBarrier` 或 `Semaphore` 类会更加适合。