在多线程编程中,内存锁是控制多个线程对共享资源访问的关键机制。Java 提供了多种内存锁的实现方式,常见的有 synchronized
、ReentrantLock
、StampedLock
和 ReadWriteLock
等。为了更好地解决分布式系统中的并发问题,我们可以将这些锁实现方式封装成一个基础的服务,并根据需要在不同的场景下使用它们。
本文将讨论 Java 内存锁的实现方式、性能比较及其适用场景,同时介绍如何封装一个基础锁服务,使其能够用于分布式锁的场景。
synchronized
synchronized
是 Java 中最基础的同步机制,通过对象监视器锁(monitor)来控制对共享资源的访问。每个对象都有一个内置的监视器锁,当线程访问被 synchronized
修饰的代码时,会自动获取锁,访问完成后自动释放锁。
优点:
synchronized
修饰方法或代码块,编写简单清晰。缺点:
适用场景:
synchronized
是一个非常不错的选择。ReentrantLock
ReentrantLock
是 java.util.concurrent.locks
包中的显式锁,它比 synchronized
更灵活,提供了更多的控制功能,例如可中断锁、定时锁等。
优点:
缺点:
synchronized
,需要手动加锁和释放锁,可能导致忘记释放锁或死锁。适用场景:
ReentrantLock
能够提供更好的性能和更灵活的控制。StampedLock
StampedLock
是 Java 8 引入的一种锁,主要为了解决读多写少场景下的锁问题。StampedLock
提供三种模式:写锁、悲观读锁、乐观读锁。
优点:
缺点:
ReentrantLock
,因为锁的升级与降级会引入额外开销。适用场景:
StampedLock
提供了较高的性能。ReadWriteLock
ReadWriteLock
是一种读写分离的锁,它允许多个线程并发读取,但写操作是独占的。ReentrantReadWriteLock
是其常见的实现。
优点:
缺点:
适用场景:
锁类型 | 适用场景 | 性能特点 | 优缺点 |
---|---|---|---|
synchronized | 简单的同步需求 | 性能良好,易于使用;高并发时性能下降 | 简单但灵活性差,性能瓶颈明显 |
ReentrantLock | 高并发锁管理 | 性能较好,灵活性强;支持中断和定时获取锁 | 如果竞争激烈,公平锁可能会带来性能下降 |
StampedLock | 读多写少的场景 | 适合读多写少的场景,乐观锁提升性能 | 写操作频繁时性能较差,锁升级和降级的操作复杂 |
ReadWriteLock | 读写分离的场景 | 适用于读多写少,提供较高的并发性能 | 写操作时性能较差,可能会导致写锁竞争 |
为了便于管理锁并提高代码的复用性,我们可以将锁的操作封装成一个基础服务。这个服务不仅能帮助我们管理内存锁,还可以扩展为分布式锁服务,适用于单机环境和分布式环境。
首先,我们定义一个通用的锁服务接口:
package com.nbsaas.boot.controller.lock;
public interface LockService {
/**
* 获取锁
*
* @param key 锁的唯一标识
* @return 是否成功获得锁
*/
boolean lock(String key);
/**
* 释放锁
*
* @param key 锁的唯一标识
* @return 是否成功释放锁
*/
boolean unlock(String key);
/**
* 判断是否已获取锁
*
* @param key 锁的唯一标识
* @return 是否已经获取锁
*/
boolean isLocked(String key);
}
lock(String key)
:尝试获取指定 key
的锁,如果成功返回 true
,否则返回 false
。unlock(String key)
:释放指定 key
的锁,返回是否成功释放锁。isLocked(String key)
:检查指定 key
的锁是否已经被获取。接下来,我们基于 LockService
接口,实现几种常见的内存锁方式。
ReentrantLock
实现ReentrantLock
是 Java 提供的一种显式锁,它是一个重入锁,支持可中断、定时获取锁等特性。
package com.nbsaas.boot.controller.lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ConcurrentHashMap;
public class ReentrantLockService implements LockService {
private final ConcurrentHashMap lockMap = new ConcurrentHashMap<>();
@Override
public boolean lock(String key) {
ReentrantLock lock = lockMap.computeIfAbsent(key, k -> new ReentrantLock());
lock.lock();
return true;
}
@Override
public boolean unlock(String key) {
ReentrantLock lock = lockMap.get(key);
if (lock != null) {
lock.unlock();
return true;
}
return false;
}
@Override
public boolean isLocked(String key) {
ReentrantLock lock = lockMap.get(key);
return lock != null && lock.isLocked();
}
}
StampedLock
实现StampedLock
是 Java 8 引入的一种锁,它提供了三种锁模式:写锁、悲观读锁和乐观读锁。适用于读多写少的场景。
package com.nbsaas.boot.controller.lock;
import java.util.concurrent.locks.StampedLock;
import java.util.concurrent.ConcurrentHashMap;
public class StampedLockService implements LockService {
private final ConcurrentHashMap lockMap = new ConcurrentHashMap<>();
@Override
public boolean lock(String key) {
StampedLock lock = lockMap.computeIfAbsent(key, k -> new StampedLock());
long stamp = lock.writeLock();
return stamp != 0;
}
@Override
public boolean unlock(String key) {
StampedLock lock = lockMap.get(key);
if (lock != null) {
lock.unlockWrite(lock.tryOptimisticRead());
return true;
}
return false;
}
@Override
public boolean isLocked(String key) {
StampedLock lock = lockMap.get(key);
return lock != null && lock.isWriteLocked();
}
}
ReadWriteLock
实现ReadWriteLock
是一种读写分离锁,它允许多个线程并发读取数据,但写操作是独占的。
package com.nbsaas.boot.controller.lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.ConcurrentHashMap;
public class ReadWriteLockService implements LockService {
private final ConcurrentHashMap lockMap = new ConcurrentHashMap<>();
@Override
public boolean lock(String key) {
ReentrantReadWriteLock lock = lockMap.computeIfAbsent(key, k -> new ReentrantReadWriteLock());
lock.writeLock().lock();
return true;
}
@Override
public boolean unlock(String key) {
ReentrantReadWriteLock lock = lockMap.get(key);
if (lock != null) {
lock.writeLock().unlock();
return true;
}
return false;
}
@Override
public boolean isLocked(String key) {
ReentrantReadWriteLock lock = lockMap.get(key);
return lock != null && lock.writeLock().isHeldByCurrentThread();
}
}
ConcurrentHashMap
的简单内存锁实现这是一个简单的锁实现,使用 ConcurrentHashMap
来管理锁的状态,适用于单机环境中小粒度的锁管理。
package com.nbsaas.boot.controller.lock;
import java.util.concurrent.ConcurrentHashMap;
public class SimpleMemoryLockService implements LockService {
private final ConcurrentHashMap lockMap = new ConcurrentHashMap<>();
@Override
public boolean lock(String key) {
return lockMap.putIfAbsent(key, true) == null;
}
@Override
public boolean unlock(String key) {
return lockMap.remove(key) != null;
}
@Override
public boolean isLocked(String key) {
return lockMap.containsKey(key);
}
}
在本文中,我们介绍了 ReentrantLock
、StampedLock
、ReadWriteLock
和基于 ConcurrentHashMap
的简单内存锁实现。每种实现都有不同的适用场景和性能特点。通过封装 LockService
接口,我们将不同类型的锁管理统一成一个标准化的服务,使得开发者可以根据不同的需求和场景选择合适的锁实现。
这种设计使得锁的管理更加灵活,可以轻松地将内存锁服务扩展到分布式环境中,比如通过改进 lock
和 unlock
方法实现分布式锁。