Java的同步方法和同步块

Java中的同步方法和同步块是用于处理多线程并发访问共享资源的重要工具。在多线程环境下,如果不加以控制,多个线程可能会同时访问和修改共享资源,导致数据的不一致性和不可预测的结果。同步方法和同步块的出现解决了这个问题,使得多线程可以安全地访问和修改共享资源。

同步方法

同步方法是指用关键字synchronized修饰的方法。当一个线程调用同步方法时,会自动获取该方法所属对象的锁,其他线程必须等待该线程释放锁后才能执行该方法。

public class Counter {
    private int count;

    public synchronized void increment() {
        count++;
    }
}

上面的代码演示了一个简单的计数器类,其中的increment方法是一个同步方法。当多个线程同时调用increment方法时,只有一个线程能够成功获取锁并执行方法体内的代码,其他线程需要等待。

同步方法的优点是使用方便,只需要在方法上加上synchronized关键字即可。然而,同步方法的粒度较大,如果一个类中有多个同步方法,那么在任意时刻只能有一个线程执行该类的任意一个同步方法,导致性能下降。

同步块

同步块是指用关键字synchronized修饰的代码块。同步块可以更细粒度地控制多线程访问共享资源的情况。同步块的锁可以是任意对象,当多个线程试图执行同一个同步块时,只有一个线程能够成功获取锁。

public class Counter {
    private int count;
    private Object lock = new Object();

    public void increment() {
        synchronized (lock) {
            count++;
        }
    }
}

上面的代码演示了一个使用同步块实现的计数器类,其中的锁是一个私有对象lock。当多个线程同时调用increment方法时,只有一个线程能够成功获取lock对象的锁,其他线程需要等待。

同步块的优点是可以更细粒度地控制同步的范围,提高了程序的执行效率。然而,同步块的使用需要手动创建锁对象,并且要保证所有访问共享资源的地方都使用同一个锁对象,否则同步块的效果将失效。

示例应用

下面的示例展示了一个使用同步方法和同步块的银行账户类。账户类有一个balance属性表示余额,depositwithdraw方法分别用于存款和取款,保证了多个线程对账户的并发访问的安全性。

public class BankAccount {
    private double balance;

    public synchronized void deposit(double amount) {
        balance += amount;
    }

    public void withdraw(double amount) {
        synchronized (this) {
            if (balance >= amount) {
                balance -= amount;
            }
        }
    }
}

上面的代码中,deposit方法使用了同步方法,而withdraw方法使用了同步块。

总结

Java的同步方法和同步块是处理多线程并发访问共享资源的重要工具。同步方法适合于粗粒度的同步控制,使用方便但性能较低。同步块适合于细粒度的同步控制,需要手动创建锁对象,但性能较高。

在实际开发中,我们应根据具体的需求选择合适的同步方式,并严格遵守同步的范围和使用规范,以保证多线程的安全性和程序的性能。

参考资料:

  • Java Concurrency in Practice - Brian Goetz et al.
  • Java synchronized block - Baeldung - [
  • Java synchronized methods - Ba