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);
    }
}

最佳实践

  1. 最小化同步范围

    • 只同步必须的代码块
    • 避免在同步块中执行耗时操作
  2. 选择合适的锁对象

    • 使用私有锁对象避免外部干扰
    • 避免使用字符串作为锁对象
  3. 避免死锁

    • 按固定顺序获取多个锁
    • 使用超时机制
    • 避免嵌套锁
  4. 考虑使用java.util.concurrent包

    • ReentrantLock提供更多功能
    • 并发集合类(ConcurrentHashMap等)
    • 原子操作类(AtomicInteger等)

注意事项

  1. synchronized是可重入的

    public class ReentrantExample {
        public synchronized void method1() {
            System.out.println("method1");
            method2(); // 可以调用另一个同步方法
        }
    
        public synchronized void method2() {
            System.out.println("method2");
        }
    }
    
  2. 等待和通知机制

    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(); // 通知所有等待的线程
        }
    }
    
  3. 不要同步基本类型的包装类

    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多线程编程的基础,正确使用它对于编写线程安全的代码至关重要。

powered by Gitbook© 2025 编外计划 | 最后修改: 2025-07-28 16:25:54

results matching ""

    No results matching ""