Java 中的可锁定对象模式:实现稳健的同步机制
也称为
- 资源锁
- 互斥对象
可锁定对象设计模式的意图
Java 中的可锁定对象模式旨在控制对多线程环境中共享资源的访问,通过提供资源锁定机制来确保线程安全,从而确保一次只有一个线程可以访问资源。
可锁定对象模式的详细解释,并附带现实世界示例
现实世界示例
想象一下繁忙办公室中的共享打印机,它类似于 Java 中可锁定对象设计模式的现实世界示例。此模式确保一次只有一个线程可以访问资源,从而维护并发控制和同步。多位员工每天都需要打印文件,但打印机一次只能处理一个打印作业。为了管理这一点,就需要一个锁定系统(类似于编程中的可锁定对象)来确保当一个人正在打印时,其他人必须等待。这可以防止打印作业重叠或相互干扰,从而确保每个文件都按顺序正确打印,这与软件开发中线程同步和资源锁定的概念相呼应。
简单来说
可锁定对象设计模式通过锁定机制来确保在多线程环境中安全访问共享资源,一次只允许一个线程通过锁定机制访问资源。
维基百科说
在计算机科学中,锁或互斥锁(来自互斥)是一种同步原语,它可以防止多个执行线程同时修改或访问状态。锁强制执行互斥并发控制策略,并且有多种可能的实现方法,针对不同的应用存在多种独特的实现方法。
Java 中可锁定对象模式的编程示例
可锁定对象模式是一种 Java 并发控制设计模式,它一次只允许一个线程访问共享资源,从而确保互斥并防止数据损坏。该模式不使用要同步的方法上的 synchronized
关键字,而是由实现 Lockable 接口的对象来处理请求。
在这个示例中,我们有一个实现 Lockable
接口的 SwordOfAragorn
对象。多个 Creature
对象(由 Elf
、Orc
和 Human
类表示)正在尝试获取这把剑。每个 Creature
都被包装在一个实现 Runnable
的 Feind
对象中,允许每个生物在单独的线程中尝试获取这把剑。
以下是 Lockable
接口
public interface Lockable {
boolean isLocked();
Creature getLocker();
boolean acquire(Creature creature);
void release(Creature creature);
}
SwordOfAragorn
类实现了此接口
public class SwordOfAragorn implements Lockable {
// Implementation details...
}
Creature
类及其子类(Elf
、Orc
、Human
)表示可以尝试获取这把剑的不同生物
public abstract class Creature {
// Implementation details...
}
public class Elf extends Creature {
// Implementation details...
}
public class Orc extends Creature {
// Implementation details...
}
public class Human extends Creature {
// Implementation details...
}
Feind
类包装一个 Creature
对象和一个 Lockable
对象,并实现 Runnable
接口
public class Feind implements Runnable {
private final Creature creature;
private final Lockable target;
public Feind(@NonNull Creature feind, @NonNull Lockable target) {
this.creature = feind;
this.target = target;
}
@Override
public void run() {
if (!creature.acquire(target)) {
fightForTheSword(creature, target.getLocker(), target);
} else {
LOGGER.info("{} has acquired the sword!", target.getLocker().getName());
}
}
// Additional methods...
}
在 App
类中,创建了多个 Feind
对象,并将其提交给 ExecutorService
,每个对象都在一个单独的线程中。
public class App implements Runnable {
@Override
public void run() {
var sword = new SwordOfAragorn();
List<Creature> creatures = new ArrayList<>();
// Creation of creatures...
ExecutorService service = Executors.newFixedThreadPool(totalFiends);
for (var i = 0; i < totalFiends; i = i + MULTIPLICATION_FACTOR) {
service.submit(new Feind(creatures.get(i), sword));
service.submit(new Feind(creatures.get(i + 1), sword));
service.submit(new Feind(creatures.get(i + 2), sword));
}
// Additional code...
}
}
此示例通过展示多个线程如何尝试获取对共享资源的锁,并且一次只能有一个线程获取锁,从而展示了可锁定对象模式。
可锁定对象模式的详细解释,并附带现实世界示例

何时在 Java 中使用可锁定对象模式
- 当需要防止多个线程同时访问共享资源而导致数据损坏时,在 Java 中使用可锁定对象模式,从而确保线程安全和健壮的共享资源管理。
- 适用于需要线程安全的系统,以及在各种操作中必须维护数据完整性的系统。
Java 中可锁定对象模式的现实世界应用
- Java 的
synchronized
关键字和java.util.concurrent.locks
包中的 Lock 接口实现了可锁定对象来管理同步。
可锁定对象模式的优缺点
优点
- 确保数据一致性并防止竞态条件。
- 为管理对共享资源的访问提供清晰的结构。
缺点
- 由于获取和释放锁的开销,可能会导致性能下降。
- 如果实现和管理不当,可能会导致死锁。