首先是key粒度的加锁工具
第一种实现:
public class ReentrantKeyLock {
public Lock getLock(String lockKey) {
Assert.notNull(lockKey, "lockKey不能为空");
return new KeyLock(lockKey);
}
public static class KeyLock implements Lock, java.io.Serializable {
//lockKey和对应的锁信息集合
private static final Map<String, LockInfo> LOCK_INFO_MAP = Maps.newHashMap();
private final String lockKey;
public KeyLock(String lockKey) {
this.lockKey = lockKey;
}
@Override
public void lock() {
while (true) {
LockInfo lockInfo = getLockInfo(lockKey);
lockInfo.lockCount.incrementAndGet();
lockInfo.lock.lock();
//如果锁以失效则重新找lockKey对应的新锁加锁
if (!lockInfo.isValid) {
lockInfo.lock.unlock();
} else {
break;
}
}
}
@Override
public void lockInterruptibly() throws InterruptedException {
while (true) {
LockInfo lockInfo = getLockInfo(lockKey);
lockInfo.lockCount.incrementAndGet();
lockInfo.lock.lockInterruptibly();
//如果锁以失效则重新找lockKey对应的新锁加锁
if (!lockInfo.isValid) {
lockInfo.lock.unlock();
} else {
break;
}
}
}
@Override
public boolean tryLock() {
while (true) {
LockInfo lockInfo = getLockInfo(lockKey);
lockInfo.lockCount.incrementAndGet();
boolean isSuccess = lockInfo.lock.tryLock();
//如果锁以失效则重新找lockKey对应的新锁加锁
if (isSuccess && !lockInfo.isValid) {
lockInfo.lock.unlock();
} else {
return isSuccess;
}
}
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
while (true) {
LockInfo lockInfo = getLockInfo(lockKey);
lockInfo.lockCount.incrementAndGet();
boolean isSuccess = lockInfo.lock.tryLock(time, unit);
//如果锁以失效则重新找lockKey对应的新锁加锁
if (isSuccess && !lockInfo.isValid) {
lockInfo.lock.unlock();
} else {
return isSuccess;
}
}
}
@Override
public void unlock() {
LockInfo lockInfo = getLockInfo(lockKey);
int lockCount = lockInfo.lockCount.decrementAndGet();
if (lockCount == 0) {
//如果加锁线程数为 0 则从LOCK_INFO_MAP移除对应的锁
LOCK_INFO_MAP.remove(lockKey);
lockInfo.isValid = false;
}
lockInfo.lock.unlock();
}
@Override
public Condition newCondition() {
return getLockInfo(lockKey).lock.newCondition();
}
private LockInfo getLockInfo(String lockKey) {
LockInfo lockInfo = LOCK_INFO_MAP.get(lockKey);
if (Objects.isNull(lockInfo)) {
synchronized (LOCK_INFO_MAP) {
lockInfo = LOCK_INFO_MAP.get(lockKey);
if (Objects.isNull(lockInfo)) {
lockInfo = new LockInfo();
LOCK_INFO_MAP.put(lockKey, lockInfo);
}
}
}
return lockInfo;
}
}
private static class LockInfo {
private final AtomicInteger lockCount;
private final ReentrantLock lock;
//如果 LockInfo 从 LOCK_INFO_MAP 移除则被改为 false ,表示该锁已经失效
private boolean isValid = true;
public LockInfo() {
lockCount = new AtomicInteger();
lock = new ReentrantLock();
}
}
}
第二种实现:思路是从Stack Overflow某人回答来的.大体代码和他类似
public class ReentrantKeyLock1 {
public Lock getLock(String lockKey) {
Assert.notNull(lockKey, "lockKey不能为空");
return new KeyLock(lockKey);
}
public static class KeyLock implements Lock, java.io.Serializable {
//lockKey和对应的锁信息集合
private static final Map<String, LockInfo> LOCK_INFO_MAP = Maps.newConcurrentMap();
private final String lockKey;
public KeyLock(String lockKey) {
this.lockKey = lockKey;
}
@Override
public void lock() {
LockInfo lockInfo = getLockInfo(true);
lockInfo.lock.lock();
}
@Override
public void lockInterruptibly() throws InterruptedException {
LockInfo lockInfo = getLockInfo(true);
lockInfo.lock.lockInterruptibly();
}
@Override
public boolean tryLock() {
LockInfo lockInfo = getLockInfo(true);
boolean isSuccess = lockInfo.lock.tryLock();
if (!isSuccess) {
cleanCacheLock(lockInfo);
}
return isSuccess;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
LockInfo lockInfo = getLockInfo(true);
boolean isSuccess = lockInfo.lock.tryLock(time, unit);
if (!isSuccess) {
cleanCacheLock(lockInfo);
}
return isSuccess;
}
@Override
public void unlock() {
LockInfo lockInfo = getLockInfo(false);
lockInfo.lock.unlock();
cleanCacheLock(lockInfo);
}
@Override
public Condition newCondition() {
return getLockInfo(false).lock.newCondition();
}
/**
* 清除 LOCK_MAP_INFO 里 lockKey 对应的数据
* 需要从 LOCK_INFO_MAP 中移除 key ,并把 currentLockInfo 设为null
*
* @param lockInfo
*/
private void cleanCacheLock(LockInfo lockInfo) {
//只有当加锁次数为 0 时才清除锁信息
if (lockInfo.lockCount.decrementAndGet() == 0) {
LOCK_INFO_MAP.compute(lockKey, (key, cacheLockInfo) -> {
if (cacheLockInfo == null || cacheLockInfo.lockCount.get() == 0) {
return null;
}
return cacheLockInfo;
});
}
}
/**
* 获取锁信息
*
* @param isLock 为 true 时,加锁次数+1
* @return LockInfo
*/
private LockInfo getLockInfo(boolean isLock) {
if (isLock) {
//currentLockInfo不存在就要创建锁,get and put 非原子操作所以需要加synchronized
return LOCK_INFO_MAP.compute(lockKey, (key, lockInfo) -> {
if (lockInfo == null) {
lockInfo = new LockInfo();
}
lockInfo.lockCount.incrementAndGet();
return lockInfo;
});
} else {
LockInfo lockInfo = LOCK_INFO_MAP.get(lockKey);
Assert.notNull(lockInfo, "可能是由于没有执行 lock 操作");
return lockInfo;
}
}
private static class LockInfo {
private final AtomicInteger lockCount;
private final ReentrantLock lock;
public LockInfo() {
lockCount = new AtomicInteger();
lock = new ReentrantLock();
}
}
}
}
上面两种不一样的地方主要是用于判断是否要从map中释放key的lockCount的自增点不一样。
下面的类主要是为了方便后面切换成redis或其他锁时不需要所有地方的代码都要改
LockManager接口:可以有不同的实现类返回redis的lock、zookeeper的lock或上面实现的ReentrantKeyLock
public interface LockManager {
/**
* 获取锁
*
* @param key 需要锁的key
* @return
*/
Lock getLock(String key);
}
KeyLockManager:返回 ReentrantKeyLock的Lock对于的LockManager
public class KeyLockManager implements LockManager {
private ReentrantKeyLock reentrantKeyLock = new ReentrantKeyLock();
@Override
public Lock getLock(String key) {
Assert.notNull(key, "key不能为空");
Lock lock = reentrantKeyLock.getLock(key);
return lock;
}
}
工厂类:
public class LockFactory {
private static LockManager lockManager;
public static void initLockManager(LockManager lockManager) {
Assert.isNull(LockFactory.lockManager, "lockManager只能初始化一次");
synchronized (LockFactory.class) {
Assert.isNull(LockFactory.lockManager, "lockManager只能初始化一次");
LockFactory.lockManager = lockManager;
}
}
public static Lock getLock(String key) {
Assert.notNull(lockManager, "请初始化lockManager");
return lockManager.getLock(key);
}
}
在spring中的使用示例
@Bean
public LockManager lockManager() {
//后面只要此处改成Redis对应的LockManager,就可以切换成redis的分布式锁.
LockManager lockManager = new KeyLockManager();
LockFactory.initLockManager(lockManager);
return lockManager;
}
public void test() {
Lock keyA = LockFactory.getLock("keyA");
keyA.lock();
System.out.println(123);
keyA.unlock();
}
当然也可以在其他生命周期创建,比如CommandListen等
测试类
public class LockTest {
public static void main(String[] args) {
LockManager lockManager = new KeyLockManager();
LockFactory.initLockManager(lockManager);
Lock keyA = LockFactory.getLock("keyA");
Lock keyB = LockFactory.getLock("keyB");
Lock keyC = LockFactory.getLock("keyC");
Lock keyD = LockFactory.getLock("keyD");
Lock keyE = LockFactory.getLock("keyE");
for (int i = 0; i <= 100000; i++) {
new MyThread("keyA", "keyA线程" + i, keyA).start();
new MyThread("keyB", "keyB线程" + i, keyB).start();
new MyThread("keyC", "keyC线程" + i, keyC).start();
new MyThread("keyD", "keyD线程" + i, keyD).start();
new MyThread("keyE", "keyE线程" + i, keyE).start();
}
// new MyThread("keyA", "keyA线程1").start();
// new MyThread("keyA", "keyA线程2").start();
// new MyThread("keyB", "keyB线程1").start();
// new MyThread("keyB", "keyB线程2").start();
}
static void sleep(int time) {
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class MyThread extends Thread {
private String key;
private String name;
static Long start = System.currentTimeMillis();
private Lock lock;
public MyThread(String key, String name, Lock lock) {
super(name);
this.key = key;
this.name = name;
this.lock = lock;
}
@SneakyThrows
@Override
public void run() {
// Lock lock = LockFactory.getLock(key);
lock.lock();
try {
System.out.println(name + "开始执行 key:" + key);
Long time = (System.currentTimeMillis() - start) / 1000;
System.out.println(name + "开始释放 key:" + key + "时间:" + time);
} finally {
lock.unlock();
}
}
}
}
如果不考虑从Map中释放key对应的锁,下面有更简单的实现.
public class JdkLockManager implements LockManager {
private Map<Object, Lock> LOCK_MAP = Maps.newHashMap();
private boolean fair;
public JdkLockManager() {
this.fair = false;
}
public JdkLockManager(boolean fair) {
this.fair = fair;
}
@Override
public Lock getLock(String key) {
Assert.notNull(key, "key不能为空");
Lock lock = LOCK_MAP.get(key);
if (Objects.isNull(lock)) {
synchronized (this) {
lock = LOCK_MAP.get(key);
if (Objects.isNull(lock)) {
lock = newLock();
//如果key已经无用就需要考虑从 LOCK_MAP 释放 key 对应的锁防止内存泄露
LOCK_MAP.put(key, lock);
}
}
}
return lock;
}
private Lock newLock() {
return new ReentrantLock(fair);
}
}