Java同步块主要通过synchronized
关键字来实现,它确保了在同一时间只有一个线程可以访问被保护的代码块或方法。以下是关于synchronized
关键字及同步块的一些关键特性:
-
四种同步块:
- 实例方法同步:通过在方法声明前添加
synchronized
关键字,使得每次只有一个线程能够调用该方法。public class MyClass { synchronized void method() { // 同步代码块 } }
- 静态方法同步:与实例方法同步类似,但锁是针对整个类而不是特定对象实例,因此同一时间内所有类实例只能有一个线程执行该静态同步方法。
public class MyClass { synchronized static void staticMethod() { // 同步代码块 } }
- 代码块同步:通过
synchronized(对象引用)
来定义一个同步代码块,指定要锁定的对象。public class MyClass { private Object lockObject = new Object(); void method() { synchronized (lockObject) { // 同步代码块 } } }
- 类级别的同步:虽然不常见,但可以通过
synchronized(MyClass.class)
锁定整个类,这等同于静态方法同步。
- 实例方法同步:通过在方法声明前添加
-
数据可见性:
synchronized
除了提供互斥访问外,还保证了内存可见性。当一个线程退出synchronized
代码块时,对共享变量所做的修改对于随后进入该同步块的其他线程是可见的,这是因为synchronized
隐式包含了“内存屏障”指令,强制刷新缓存中的数据到主内存,并从主内存中加载最新的值。
-
指令重排(Reordering):
- 在多线程环境下,JVM和CPU为了优化可能会进行指令重排,但这可能导致并发问题。使用
synchronized
可以防止这种情况,因为它会插入必要的内存屏障以维持程序的顺序一致性。
- 在多线程环境下,JVM和CPU为了优化可能会进行指令重排,但这可能导致并发问题。使用
-
性能开销:
- 使用
synchronized
有一定的性能开销,因为它需要获取和释放锁,这涉及到系统调用、上下文切换等操作。尤其在高竞争条件下,过度或者不当使用synchronized
可能导致性能瓶颈。
- 使用
-
可重入性(Reentrancy):
- Java中的
synchronized
具有可重入性,这意味着同一个线程可以多次获得同一把锁。如果一个线程已经获得了某个对象的锁,那么它在没有释放这个锁之前还可以再次进入由同一个锁保护的代码块或方法,而不会阻塞自己。
- Java中的
总结来说,Java的synchronized
关键字用于解决多线程环境下的数据同步和可见性问题,但它并非无代价的解决方案,所以在实际编程中需根据具体需求权衡其利弊并合理使用。