在 Java 中,synchronized
关键字用于控制对共享资源的访问,确保在多个线程访问这些资源时能够保持数据的一致性和线程安全性。它是实现多线程程序中同步机制的重要工具。
synchronized 的基本用法
synchronized
关键字可以用在方法和代码块中,来保证在同一时刻只有一个线程可以执行被 synchronized
修饰的部分。
1. 修饰实例方法
当使用 synchronized
修饰实例方法时,锁定的是当前对象实例(即 this
)。也就是说,同一时刻只有一个线程可以访问该实例的同步方法。
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
在上述示例中,increment
和 getCount
方法都是同步的。当一个线程在调用 increment
时,其他线程必须等待这个线程完成才能访问这两个方法,从而保证了 count
变量在多线程环境中的线程安全性。
2. 修饰静态方法
当 synchronized
修饰静态方法时,锁定的是该类的 Class
对象,这意味着所有实例共享这个锁。
class StaticCounter {
private static int count = 0;
public static synchronized void increment() {
count++;
}
public static synchronized int getCount() {
return count;
}
}
在这个示例中,increment
和 getCount
是静态同步方法,任何时候只能有一个线程在执行这些方法,确保了 count
的共享访问是安全的。
3. 修饰代码块
synchronized
还可以用于修饰代码块。通过指定锁对象,可以更加灵活地控制同步的粒度。
class Counter {
private int count = 0;
public void increment() {
synchronized (this) {
count++;
}
}
public int getCount() {
synchronized (this) {
return count;
}
}
}
在这个示例中,synchronized
关键字修饰了代码块,只有在执行 count++
和 return count
这段代码时才会获取锁,从而将锁定范围缩小,提高并发性能。
synchronized 的优势与不足
优势
- 简单易用:使用
synchronized
关键字很简单,不需要额外的同步工具。 - 保证可见性:在获取锁的过程中,JVM 会保证对共享变量的修改在释放锁之前对其他线程可见。
不足
- 性能问题:如果锁的竞争激烈,可能会导致线程的上下文切换,进而影响性能。
- 死锁风险:不当使用可能导致死锁现象,特别是在多个线程相互等待对方释放锁的情况下。
总结
synchronized
是 Java 提供的一个重要机制,用于实现线程安全和共享资源的同步。在使用时需要注意锁的粒度,避免性能问题;同时要小心避免死锁的发生。在实际开发中,合理地使用同步机制可以有效提高程序的执行效率和稳定性。随着 Java 8 及更高版本的引入,也提供了更高级的并发工具,比如 ReentrantLock
和 java.util.concurrent
包下的其他工具,这些可以作为 synchronized
的补充和替代。在需要复杂的同步控制时,可以考量使用这些更灵活的并发工具。