跳转到内容

Java synchronized关键字

来自代码酷

Java synchronized关键字[编辑 | 编辑源代码]

简介[编辑 | 编辑源代码]

synchronized是Java中用于实现线程同步的关键字,它提供了一种简单的机制来确保多个线程在访问共享资源时的互斥性。当某个方法或代码块被声明为synchronized时,同一时间只能有一个线程执行该代码,从而避免竞态条件(Race Condition)和数据不一致问题。

synchronized可以用于:

  • 实例方法
  • 静态方法
  • 代码块(需要指定锁对象)

工作原理[编辑 | 编辑源代码]

Java中的每个对象都有一个内置锁(也称为监视器锁)。当线程进入synchronized方法或代码块时,它会自动获取该对象的锁;当方法或代码块执行完毕时,锁会自动释放。如果另一个线程试图获取已被占用的锁,它将被阻塞,直到锁被释放。

flowchart TD A[线程尝试进入synchronized代码] --> B{锁是否可用?} B -->|是| C[获取锁并执行代码] B -->|否| D[线程进入阻塞状态] C --> E[释放锁] D -->|锁可用时| C

使用方法[编辑 | 编辑源代码]

同步实例方法[编辑 | 编辑源代码]

当synchronized修饰实例方法时,锁对象是该方法所属的实例(this)。

public class Counter {
    private int count = 0;
    
    // 同步实例方法
    public synchronized void increment() {
        count++;
    }
    
    public int getCount() {
        return count;
    }
}

同步静态方法[编辑 | 编辑源代码]

当synchronized修饰静态方法时,锁对象是该类的Class对象(如Counter.class)。

public class StaticCounter {
    private static int count = 0;
    
    // 同步静态方法
    public static synchronized void increment() {
        count++;
    }
    
    public static int getCount() {
        return count;
    }
}

同步代码块[编辑 | 编辑源代码]

可以指定任意对象作为锁,提供更细粒度的控制。

public class FineGrainedLock {
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();
    private int value1 = 0;
    private int value2 = 0;
    
    public void increment1() {
        synchronized(lock1) {
            value1++;
        }
    }
    
    public void increment2() {
        synchronized(lock2) {
            value2++;
        }
    }
}

实际案例[编辑 | 编辑源代码]

银行账户转账[编辑 | 编辑源代码]

考虑一个银行账户转账场景,需要确保转账操作的原子性:

public class BankAccount {
    private double balance;
    
    public BankAccount(double initialBalance) {
        this.balance = initialBalance;
    }
    
    // 同步方法确保转账操作的线程安全
    public synchronized void transfer(BankAccount to, double amount) {
        if (this.balance >= amount) {
            this.balance -= amount;
            to.balance += amount;
            System.out.println("转账成功: " + amount);
        } else {
            System.out.println("余额不足");
        }
    }
    
    public synchronized double getBalance() {
        return balance;
    }
}

生产者-消费者问题[编辑 | 编辑源代码]

使用synchronized实现简单的生产者-消费者模型:

public class SharedQueue {
    private final Queue<Integer> queue = new LinkedList<>();
    private final int capacity;
    
    public SharedQueue(int capacity) {
        this.capacity = capacity;
    }
    
    // 生产者方法
    public synchronized void produce(int item) throws InterruptedException {
        while (queue.size() == capacity) {
            wait(); // 队列满时等待
        }
        queue.add(item);
        notifyAll(); // 通知消费者
    }
    
    // 消费者方法
    public synchronized int consume() throws InterruptedException {
        while (queue.isEmpty()) {
            wait(); // 队列空时等待
        }
        int item = queue.remove();
        notifyAll(); // 通知生产者
        return item;
    }
}

性能考虑[编辑 | 编辑源代码]

虽然synchronized提供了简单的线程安全机制,但过度使用可能导致性能问题:

  • 锁竞争会增加线程上下文切换
  • 可能导致死锁
  • 同步范围过大可能降低并发性

优化建议:

  • 尽量减小同步代码块的范围
  • 考虑使用更高级的并发工具(如java.util.concurrent包中的类)
  • 对于读多写少的场景,考虑使用读写锁(ReentrantReadWriteLock)

常见问题[编辑 | 编辑源代码]

synchronized vs volatile[编辑 | 编辑源代码]

特性 synchronized volatile
原子性 保证 不保证(除单个读/写操作)
可见性 保证 保证
互斥性 保证 不保证
性能 较低 较高

可重入性[编辑 | 编辑源代码]

Java中的synchronized锁是可重入的,即一个线程可以多次获取同一个锁:

public class ReentrantExample {
    public synchronized void method1() {
        method2(); // 可以调用另一个同步方法
    }
    
    public synchronized void method2() {
        // ...
    }
}

数学原理[编辑 | 编辑源代码]

从理论上讲,synchronized实现了互斥锁(Mutex),可以用以下公式表示:

{锁状态={1,锁可用0,锁被占用获取锁:while(锁状态==0);锁状态=0释放锁:锁状态=1

总结[编辑 | 编辑源代码]

synchronized关键字是Java线程同步的基础工具,它:

  • 提供简单易用的线程安全机制
  • 支持方法级和代码块级同步
  • 保证可见性和原子性
  • 具有可重入特性

对于复杂的并发场景,建议结合java.util.concurrent包中的高级工具使用,但在许多情况下,正确使用synchronized已经足够满足线程安全需求。