@Synchronized
synchronized
的正确用法:不要暴露你的锁。
概述
@Synchronized
是 synchronized
方法修饰符更安全的变体。与 synchronized
类似,此注解只能用于静态方法和实例方法。它的运作方式与 synchronized
关键字类似,但它锁定在不同的对象上。关键字锁定在 this
上,而注解锁定在名为 $lock
的私有字段上。
如果该字段不存在,则会自动为你创建。如果你注解的是 static
方法,则注解会锁定在名为 $LOCK
的静态字段上。
如果需要,你可以自己创建这些锁。如果你已经自己创建了 $lock
和 $LOCK
字段,当然就不会再生成它们。你也可以选择锁定在另一个字段上,方法是将该字段指定为 @Synchronized
注解的参数。在这种用法变体中,字段不会自动创建,你必须显式地自己创建它们,否则会发出错误。
锁定在 this
或你自己的类对象上可能会产生不幸的副作用,因为不受你控制的其他代码也可以锁定在这些对象上,这可能会导致竞态条件和其他与线程相关的讨厌的错误。
如果你更喜欢 java.util.concurrent.locks
风格的锁(如果你正在使用虚拟线程,则推荐使用),请查看 @Locked
。
使用 Lombok
import lombok.Synchronized;
|
原生 Java
public class SynchronizedExample {
|
支持的配置键
-
lombok.synchronized.flagUsage
= [warning
|error
] (默认值:未设置) - 如果配置了,Lombok 将把任何
@Synchronized
的用法标记为警告或错误。
小字说明
如果 $lock
和/或 $LOCK
是自动生成的,则这些字段将使用空的 Object[]
数组初始化,而不是像大多数展示这种模式的片段那样,仅仅使用 new Object()
。 Lombok 这样做是因为新的对象是 *不可* 序列化的,但 0 大小的数组是可序列化的。因此,使用 @Synchronized
不会阻止你的对象被序列化。
在你的类中至少有一个 @Synchronized
方法意味着会有一个锁字段,但如果你稍后删除所有此类方法,将不再有锁字段。这意味着你预定的 serialVersionUID
会发生变化。我们建议你,如果你打算通过 Java 的序列化机制长期存储你的类, *始终* 为你的类添加 serialVersionUID
。如果你这样做,从你的方法中删除所有 @Synchronized
注解将不会破坏序列化。
如果你想知道为什么当你为你自己的锁对象选择自己的名称时,字段不会自动生成:因为否则在字段名称中出现拼写错误将导致一个 *非常* 难以发现的错误!