synchronized 关键字
概述
synchronized
关键字用于实现线程同步,确保同一时间只有一个线程可以访问被保护的代码块或方法,防止多线程并发访问导致的数据不一致问题。
语法格式
同步方法
public synchronized void method() {
// 方法体
}
public static synchronized void staticMethod() {
// 静态同步方法
}
同步代码块
synchronized (锁对象) {
// 需要同步的代码
}
基本用法
同步方法
public class Counter {
private int count = 0;
// 同步实例方法 - 锁定当前对象实例
public synchronized void increment() {
count++;
System.out.println("当前计数: " + count);
}
// 同步实例方法
public synchronized void decrement() {
count--;
System.out.println("当前计数: " + count);
}
// 同步获取方法
public synchronized int getCount() {
return count;
}
// 静态同步方法 - 锁定类对象
private static int globalCount = 0;
public static synchronized void incrementGlobal() {
globalCount++;
System.out.println("全局计数: " + globalCount);
}
public static synchronized int getGlobalCount() {
return globalCount;
}
}
// 测试同步方法
public class SynchronizedMethodExample {
public static void main(String[] args) {
Counter counter = new Counter();
// 创建多个线程同时访问计数器
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
counter.increment();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}, "线程1");
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
counter.increment();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}, "线程2");
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("最终计数: " + counter.getCount());
// 测试静态同步方法
Counter.incrementGlobal();
Counter.incrementGlobal();
System.out.println("全局计数: " + Counter.getGlobalCount());
}
}
同步代码块
public class BankAccount {
private double balance;
private final Object lock = new Object(); // 专用锁对象
public BankAccount(double initialBalance) {
this.balance = initialBalance;
}
// 使用synchronized代码块
public void deposit(double amount) {
synchronized (lock) {
double oldBalance = balance;
System.out.println(Thread.currentThread().getName() +
" 存款前余额: " + oldBalance);
// 模拟处理时间
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
balance += amount;
System.out.println(Thread.currentThread().getName() +
" 存款 " + amount + ",余额: " + balance);
}
}
public void withdraw(double amount) {
synchronized (lock) {
if (balance >= amount) {
double oldBalance = balance;
System.out.println(Thread.currentThread().getName() +
" 取款前余额: " + oldBalance);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
balance -= amount;
System.out.println(Thread.currentThread().getName() +
" 取款 " + amount + ",余额: " + balance);
} else {
System.out.println(Thread.currentThread().getName() +
" 余额不足,无法取款 " + amount);
}
}
}
public double getBalance() {
synchronized (lock) {
return balance;
}
}
}
// 测试同步代码块
public class SynchronizedBlockExample {
public static void main(String[] args) {
BankAccount account = new BankAccount(1000.0);
// 创建多个线程进行存取款操作
Thread depositor1 = new Thread(() -> {
account.deposit(200);
account.deposit(300);
}, "存款线程1");
Thread depositor2 = new Thread(() -> {
account.deposit(150);
account.deposit(250);
}, "存款线程2");
Thread withdrawer1 = new Thread(() -> {
account.withdraw(400);
account.withdraw(100);
}, "取款线程1");
Thread withdrawer2 = new Thread(() -> {
account.withdraw(200);
account.withdraw(300);
}, "取款线程2");
depositor1.start();
depositor2.start();
withdrawer1.start();
withdrawer2.start();
try {
depositor1.join();
depositor2.join();
withdrawer1.join();
withdrawer2.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("最终余额: " + account.getBalance());
}
}
生产者-消费者模式
import java.util.LinkedList;
import java.util.Queue;
public class ProducerConsumerExample {
private final Queue<Integer> buffer = new LinkedList<>();
private final int capacity = 5;
// 生产者方法
public void produce(int item) throws InterruptedException {
synchronized (this) {
// 当缓冲区满时等待
while (buffer.size() == capacity) {
System.out.println("缓冲区已满,生产者等待...");
wait(); // 释放锁并等待
}
buffer.offer(item);
System.out.println("生产者生产: " + item + ",缓冲区大小: " + buffer.size());
// 通知所有等待的消费者
notifyAll();
}
}
// 消费者方法
public int consume() throws InterruptedException {
synchronized (this) {
// 当缓冲区空时等待
while (buffer.isEmpty()) {
System.out.println("缓冲区为空,消费者等待...");
wait(); // 释放锁并等待
}
int item = buffer.poll();
System.out.println("消费者消费: " + item + ",缓冲区大小: " + buffer.size());
// 通知所有等待的生产者
notifyAll();
return item;
}
}
public static void main(String[] args) {
ProducerConsumerExample example = new ProducerConsumerExample();
// 创建生产者线程
Thread producer = new Thread(() -> {
try {
for (int i = 1; i <= 10; i++) {
example.produce(i);
Thread.sleep(200);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "生产者");
// 创建消费者线程
Thread consumer = new Thread(() -> {
try {
for (int i = 1; i <= 10; i++) {
example.consume();
Thread.sleep(300);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "消费者");
producer.start();
consumer.start();
try {
producer.join();
consumer.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
锁的粒度控制
public class FinegrainedLocking {
private final Object readLock = new Object();
private final Object writeLock = new Object();
private String data = "初始数据";
// 细粒度锁 - 分别控制读写操作
public String readData() {
synchronized (readLock) {
System.out.println(Thread.currentThread().getName() + " 读取数据: " + data);
return data;
}
}
public void writeData(String newData) {
synchronized (writeLock) {
System.out.println(Thread.currentThread().getName() + " 写入数据: " + newData);
this.data = newData;
}
}
// 同时需要读写锁的操作
public void updateData(String prefix) {
// 按固定顺序获取锁,避免死锁
synchronized (readLock) {
synchronized (writeLock) {
String oldData = this.data;
this.data = prefix + oldData;
System.out.println(Thread.currentThread().getName() +
" 更新数据: " + oldData + " -> " + this.data);
}
}
}
}
死锁示例和预防
public class DeadlockExample {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
// 可能导致死锁的方法
public void method1() {
synchronized (lock1) {
System.out.println(Thread.currentThread().getName() + " 获取lock1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + " 获取lock2");
}
}
}
public void method2() {
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + " 获取lock2");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
synchronized (lock1) {
System.out.println(Thread.currentThread().getName() + " 获取lock1");
}
}
}
// 预防死锁 - 按固定顺序获取锁
public void safeMethod1() {
synchronized (lock1) {
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + " 安全执行method1");
}
}
}
public void safeMethod2() {
synchronized (lock1) { // 与safeMethod1相同的获取顺序
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + " 安全执行method2");
}
}
}
public static void main(String[] args) {
DeadlockExample example = new DeadlockExample();
// 演示死锁(注意:这可能导致程序卡死)
/*
Thread t1 = new Thread(() -> example.method1(), "线程1");
Thread t2 = new Thread(() -> example.method2(), "线程2");
t1.start();
t2.start();
*/
// 演示无死锁的版本
Thread t1 = new Thread(() -> example.safeMethod1(), "安全线程1");
Thread t2 = new Thread(() -> example.safeMethod2(), "安全线程2");
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
锁机制原理
1. 对象监视器锁
- 每个Java对象都有一个监视器锁(monitor lock)
- synchronized方法锁定的是调用对象的监视器
- 静态synchronized方法锁定的是类对象的监视器
2. 锁的获取和释放
public class LockMechanism {
public synchronized void synchronizedMethod() {
// 1. 进入方法时自动获取对象锁
// 2. 执行方法体
// 3. 正常退出或异常退出时自动释放锁
}
public void synchronizedBlock() {
// 其他代码(不需要同步)
synchronized (this) {
// 1. 进入代码块时获取对象锁
// 2. 执行代码块
// 3. 退出代码块时释放锁
}
// 其他代码(不需要同步)
}
}
性能考虑
1. 锁的开销
public class PerformanceConsideration {
private int counter = 0;
// 较重的同步方法
public synchronized void heavySynchronizedMethod() {
// 整个方法都被同步
counter++;
// 大量不需要同步的计算
for (int i = 0; i < 1000; i++) {
Math.sqrt(i);
}
}
// 更轻量的同步
public void lightweightSynchronization() {
// 只同步必要的部分
for (int i = 0; i < 1000; i++) {
Math.sqrt(i);
}
synchronized (this) {
counter++; // 只同步关键操作
}
}
}
2. 避免过度同步
public class AvoidOverSynchronization {
private final List<String> list = new ArrayList<>();
// 不好的做法 - 过度同步
public synchronized void badAddAndProcess(String item) {
list.add(item);
processItem(item); // 这个处理不需要同步
}
// 好的做法 - 最小化同步范围
public void goodAddAndProcess(String item) {
synchronized (this) {
list.add(item); // 只同步必要的操作
}
processItem(item); // 在同步块外处理
}
private void processItem(String item) {
// 耗时的处理逻辑
System.out.println("处理: " + item);
}
}
最佳实践
最小化同步范围:
- 只同步必须的代码块
- 避免在同步块中执行耗时操作
选择合适的锁对象:
- 使用私有锁对象避免外部干扰
- 避免使用字符串作为锁对象
避免死锁:
- 按固定顺序获取多个锁
- 使用超时机制
- 避免嵌套锁
考虑使用java.util.concurrent包:
- ReentrantLock提供更多功能
- 并发集合类(ConcurrentHashMap等)
- 原子操作类(AtomicInteger等)
注意事项
synchronized是可重入的:
public class ReentrantExample { public synchronized void method1() { System.out.println("method1"); method2(); // 可以调用另一个同步方法 } public synchronized void method2() { System.out.println("method2"); } }
等待和通知机制:
public class WaitNotifyExample { private boolean condition = false; public synchronized void waitForCondition() throws InterruptedException { while (!condition) { wait(); // 释放锁并等待 } System.out.println("条件满足,继续执行"); } public synchronized void setCondition() { condition = true; notifyAll(); // 通知所有等待的线程 } }
不要同步基本类型的包装类:
public class WrapperSynchronization { private Integer count = 0; public void badIncrement() { synchronized (count) { // 危险:count可能被重新分配 count++; // 这会创建新的Integer对象 } } private final Object lock = new Object(); public void goodIncrement() { synchronized (lock) { // 安全:使用固定的锁对象 count++; } } }
synchronized关键字是Java多线程编程的基础,正确使用它对于编写线程安全的代码至关重要。