Advertisement

浅谈 Synchronized 锁原理和优化

阅读量:

一、Synchronized 简介

Synchronized 是 Java 提供的一种用于实现线程同步的关键机制,在程序设计中发挥着重要作用。每个对象都内建有一个内部锁(也称为监视器锁或互斥锁),这种机制可被 Synchronized 关键字所捕获以实现对共享资源的安全访问控制。一旦某个线程捕获了该锁,则所有试图访问同一共享资源的相关操作都会被阻塞直到当前操作完成并释放该锁为止。

二、Synchronized 的实现原理

Synchronized关键字的功能是以Java对象头部中的特定标记来实现的。Java的对象头部包含有两个关键信息:一个是对象哈希码值域中的唯一标识符;另一个是与锁定相关的lock state字段的信息存储区域。其中lock state包含两方面的信息:一是当前是否被其他线程所封锁;二是封锁该对象的具体线程ID标识符。一旦某一线程成功获取目标对象的所有权时,在JVM中会更新该对象头部中的lock state字段,并记录当前操作完成者的线索数据域内容。若其他任何在线程试图重新获取该已被占用的对象所有权时,则会立即陷入等待状态。完成上述操作后,在后续步骤中,JVM系统会自动释放该标记,并解除所有等待在此处等待的任务

由于 Synchronized 的实现基于对象头上的锁状态检查机制, 因此它无法直接锁定基本类型. 当目标是锁定基本类型时, 可以参考 Java 中提供的多种锁机制, 其中 ReentrantLock 是一种常用选择.

三、Synchronized 的优化

1. 减小锁的粒度

独占式同步机制是一种专为多线程环境设计的数据结构。它通过确保在同一时间点上只有一个线程能够成功地锁定该资源来实现互斥访问。当一个方法仅包含一小部分代码需进行同步操作时,在不影响整体效率的前提下将其细分为多个较小的互斥区域会是一个有效的策略。这样做不仅能够减少资源被占用的时间长度(即降低其粒度),还能显著提高系统的吞吐量和响应速度。

public final void method() {
synchronized lock = this; {
// 被 synchronized 的代码块
}
// 被自动+synchronized 的代码块
}

在上述代码中,采用了 this 对象充当锁。然而仅有特定代码块需进行同步操作。建议应将其单独放置于 synchronized 区域内,并将无需同步的部分置于外部 synchronized 区域以减少其粒度程度。

2. 使用局部变量代替成员变量

当一个对象包含多个成员变量时(即存在部分成员变量需要同步操作),我们可以采用局部变量来替代这些不需要全局同步的成员变量(从而降低锁的粒度)。举个例子来说,在这种情况下选择局部变量能够显著提升系统的性能表现)。

实现方法名为method public void

创建对象lock1并赋值给新Object实例

创建对象lock2并赋值给新Object实例

对lock1施加同步控制

不需要同步的代码

对lock2施加同步控制

上述代码中,在实现互斥控制时采用了两个独立的锁实例 lock1 和 lock2,并分别实现了对不同区域代码块的互斥控制。通过采用独立锁机制降低了锁的时间开销,并在此基础上提升了系统的并发处理能力。

3. 使用 ConcurrentHashMap

当多个 line 线 thread 尝试对同一 Map 执行操作时, 选择用 ConcurrentHashMap 替代 HashMap 可以消除了多 line 线 thread 对 HashMap 进行操作时可能导致的 line 线 thread 安全问题. 其内部采用了多个互斥锁机制, 每个 lock 负责锁定特定的数据区域, 因此在多 line 线 thread 的环境下, 不同的 line 线 thread 可以同时操作各自的 data 区域, 最终提升了系统的并发效率.

4. 使用读写锁

如果一个系统的读操作频率显著高于其写的频率,则可以通过采用ReadWrite locks机制来有效提升并发处理效率。ReadWrite locks是一种经典的同步机制,在这种机制下,多个线程可共享同一数据对象但不会产生冲突(即多个线程可获取read lock),而write lock是互斥使用的(即只有一个线程能获取write lock)。例如,在高并发应用中设置10个虚拟进程,并限定每个进程的最大等待时间为5秒,则可以在保证数据一致性的同时实现较高的吞吐量。

private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final Map<Object, String> map = new HashMap<>();

public static Object getValue(String key) {
lock.lock();
try {
map.get(key);
return this;
} finally {
lock.unlock();
}
}

public void setValue(String identifier, Object value) {
lock.withWriteOperation().acquire();
try {
map.put(identifier, value);
} finally {
unlock the write operation;
}
}

在本代码中,
该方法采用了读锁机制,
允许多个线程并发访问,
相比之下,在putValue方法中采用了write lock mechanism,
仅允许一个线程执行更新操作

5. 使用 synchronized 关键字和 Lock 接口的性能对比

Synchronized 和 Lock 接口都可以用于线程同步,在其运行机制上存在显著差异,并对程序运行效率产生明显影响。基于 Java 并发编程实践中的实证研究结果表明,在低负载场景下使用 synchronized 关键字相比 Lock 接口可带来更好的性能表现;然而,在处理高并发任务时,则建议选用非公平锁机制以提升系统效能。

四、总结

Java 提供了 Synchronized 作为一种常见的线程同步机制。该机制通过对象头上的锁状态设计来实现对共享资源的安全访问控制。当采用 Synchronized 关键字时,在优化性能时可以考虑减小锁粒度或采用局部变量替代成员变量等方法来提高效率。例如,在优化性能时可以考虑减小锁粒度或采用局部变量替代成员变量等方法来提高效率。相比之下,在低并发场景下可以考虑使用 Lock 接口以获得更好的性能表现;而在高并发场景下则应优先选择非公平锁实现以确保较好的性能效果。因此,在实际开发中建议根据具体情况选择合适的同步策略,并尽量减少不必要的同步操作以避免增加程序复杂度和运行开销。

除了提供Synchronized和Lock接口之外,在Java语言中还提供了其他几种常见的线程同步机制如Semaphore、CountDownLatch以及CyclicBarrier等。每一种机制都具有其独特的特点以及适用场景,在实际开发过程中开发者应根据具体需求选择最适合的线程同步方案,并以提升程序运行效率与系统可靠性为目标

全部评论 (0)

还没有任何评论哟~