相信大家都用过Redis分布式锁吧 Redis分布式锁是对某个字符串来进行上锁的 用起来嘎嘎爽
于是我就想能不能自己实现一个根据key来同步的锁?下面为该锁的实现过程
若有线程安全问题或者是讲解不到位的地方,欢迎各位大佬指正。
话不多说直接开搞
首先先写一个锁
static final class Lock extends ReentrantLock {
/**
* 正在使用或者是准备使用该锁的数量
*/
int using;
/**
* 锁的有参构造函数
*
* @param fair 是否公平
*/
Lock(boolean fair) {
super(fair);
}
}
这个类比较简单 继承自java.util.concurrent.locks.ReentrantLock
然后给他一个using属性
KeyLock
package cn.liziguo.util.concurrent;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author Liziguo
* @date 2022/8/12 11:15
*/
public class KeyLock<K> {
final ConcurrentHashMap<K, Lock> map = new ConcurrentHashMap<>();
/**
* 是否公平锁
*/
final boolean fair;
/**
* 无参构造函数 默认使用非公平锁
*/
public KeyLock() {
fair = false;
}
/**
* 有参构造函数指定是否使用公平锁
*
* @param fair 是否公平
*/
public KeyLock(boolean fair) {
this.fair = fair;
}
/**
* 从map里获取锁 如果存在则返回 不存在则创建
* 并使其using++
*
* @param key key
* @return lock
*/
Lock getLock(K key) {
return map.compute(key, (k, lock) -> {
if (lock == null) {
lock = new Lock(fair);
}
lock.using++;
return lock;
});
}
/**
* 获取锁 会阻塞
*
* @param key key
*/
public void lock(K key) {
getLock(key).lock();
}
/**
* 尝试获取锁 不阻塞
*
* @param key key
* @return 获取成功返回true
*/
public boolean tryLock(K key) {
AtomicBoolean b = new AtomicBoolean();
map.compute(key, (k, lock) -> {
if (lock == null) {
lock = new Lock(fair);
}
// 由于tryLock是非阻塞的 我们可以直接在map里进行调用
if (lock.tryLock()) {
// 只有申请成功了才使using++
lock.using++;
// 把结果传递到外部
b.set(true);
}
return lock;
});
return b.get();
}
/**
* 尝试获取锁 并指定时间
*
* @param key key
* @param timeout 过期时间
* @param unit 时间单位
* @return 获取成功返回true
*/
public boolean tryLock(K key, long timeout, TimeUnit unit) {
Lock lock = getLock(key);
boolean b;
try {
b = lock.tryLock(timeout, unit);
} catch (InterruptedException e) {
b = false;
}
if (!b) {
// 如果锁获取失败 则判断该锁是否被其他进程使用
map.computeIfPresent(key, (k, oldLock) -> {
// 看看别的地方有没有用着这个锁
if (--oldLock.using == 0) {
// 如果没有 就释放内存
return null;
} else {
// 否则不管
return oldLock;
}
});
}
return b;
}
/**
* 释放锁 必须由申请锁的线程进行调用
*
* @param key key
*/
public void unlock(K key) {
map.computeIfPresent(key, (k, lock) -> {
// 释放锁
lock.unlock();
// 看看别的地方有没有用着这个锁
if (--lock.using == 0) {
// 如果没有 就释放内存
return null;
} else {
// 否则不管
return lock;
}
});
}
static final class Lock extends ReentrantLock {
/**
* 正在使用或者是准备使用该锁的数量
*/
int using;
/**
* 锁的有参构造函数
*
* @param fair 是否公平
*/
Lock(boolean fair) {
super(fair);
}
}
}
主要是借助java.util.concurrent.ConcurrentHashMap
处理线程安全问题
用法
现在这个类的基本功能已经实现并且可以使用了
用法也很简单:
public static void main(String[] args) {
KeyLock<String> keyLock = new KeyLock<>();
final String key = "lock:user_id:" + 10001;
keyLock.lock(key);
try {
System.out.println("哈哈哈");
} finally {
keyLock.unlock(key);
}
}
扩展api
在此基础上提供几个api提高易用性
public void lockExecute(K key, Runnable runnable) {
lock(key);
try {
runnable.run();
} finally {
unlock(key);
}
}
public <T> T lockExecute(K key, Supplier<T> supplier) {
lock(key);
try {
return supplier.get();
} finally {
unlock(key);
}
}
public boolean tryLockExecute(K key, Runnable runnable) {
if (tryLock(key)) {
try {
runnable.run();
return true;
} finally {
unlock(key);
}
}
return false;
}
public <T> T tryLockExecute(K key, T failResult, Supplier<T> supplier) {
if (tryLock(key)) {
try {
return supplier.get();
} finally {
unlock(key);
}
}
return failResult;
}
用法:
public static void main(String[] args) {
KeyLock<String> keyLock = new KeyLock<>();
final String key = "lock:user_id:" + 10001;
keyLock.lockExecute(key, () -> System.out.println("哈哈哈"));
}
完整代码
package cn.liziguo.util.concurrent;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.*;
/**
* @author Liziguo
* @date 2022/8/12 11:15
*/
public class KeyLock<K> {
final ConcurrentHashMap<K, Lock> map = new ConcurrentHashMap<>();
/**
* 是否公平锁
*/
final boolean fair;
/**
* 无参构造函数 默认使用非公平锁
*/
public KeyLock() {
fair = false;
}
/**
* 有参构造函数指定是否使用公平锁
*
* @param fair 是否公平
*/
public KeyLock(boolean fair) {
this.fair = fair;
}
/**
* 从map里获取锁 如果存在则返回 不存在则创建
* 并使其using++
*
* @param key key
* @return lock
*/
Lock getLock(K key) {
return map.compute(key, (k, lock) -> {
if (lock == null) {
lock = new Lock(fair);
}
lock.using++;
return lock;
});
}
/**
* 获取锁 会阻塞
*
* @param key key
*/
public void lock(K key) {
getLock(key).lock();
}
/**
* 尝试获取锁 不阻塞
*
* @param key key
* @return 获取成功返回true
*/
public boolean tryLock(K key) {
AtomicBoolean b = new AtomicBoolean();
map.compute(key, (k, lock) -> {
if (lock == null) {
lock = new Lock(fair);
}
// 由于tryLock是非阻塞的 我们可以直接在map里进行调用
if (lock.tryLock()) {
// 只有申请成功了才使using++
lock.using++;
// 把结果传递到外部
b.set(true);
}
return lock;
});
return b.get();
}
/**
* 尝试获取锁 并指定时间
*
* @param key key
* @param timeout 过期时间
* @param unit 时间单位
* @return 获取成功返回true
*/
public boolean tryLock(K key, long timeout, TimeUnit unit) {
Lock lock = getLock(key);
boolean b;
try {
b = lock.tryLock(timeout, unit);
} catch (InterruptedException e) {
b = false;
}
if (!b) {
// 如果锁获取失败 则判断该锁是否被其他进程使用
map.computeIfPresent(key, (k, oldLock) -> {
// 看看别的地方有没有用着这个锁
if (--oldLock.using == 0) {
// 如果没有 就释放内存
return null;
} else {
// 否则不管
return oldLock;
}
});
}
return b;
}
/**
* 释放锁 必须由申请锁的线程进行调用
*
* @param key key
*/
public void unlock(K key) {
map.computeIfPresent(key, (k, lock) -> {
// 释放锁
lock.unlock();
// 看看别的地方有没有用着这个锁
if (--lock.using == 0) {
// 如果没有 就释放内存
return null;
} else {
// 否则不管
return lock;
}
});
}
public void lockExecute(K key, Runnable runnable) {
lock(key);
try {
runnable.run();
} finally {
unlock(key);
}
}
public boolean lockExecute(K key, BooleanSupplier supplier) {
lock(key);
try {
return supplier.getAsBoolean();
} finally {
unlock(key);
}
}
public int lockExecute(K key, IntSupplier supplier) {
lock(key);
try {
return supplier.getAsInt();
} finally {
unlock(key);
}
}
public long lockExecute(K key, LongSupplier supplier) {
lock(key);
try {
return supplier.getAsLong();
} finally {
unlock(key);
}
}
public double lockExecute(K key, DoubleSupplier supplier) {
lock(key);
try {
return supplier.getAsDouble();
} finally {
unlock(key);
}
}
public <T> T lockExecute(K key, Supplier<T> supplier) {
lock(key);
try {
return supplier.get();
} finally {
unlock(key);
}
}
public boolean tryLockExecute(K key, Runnable runnable) {
if (tryLock(key)) {
try {
runnable.run();
return true;
} finally {
unlock(key);
}
}
return false;
}
public boolean tryLockExecute(K key, boolean failResult, BooleanSupplier supplier) {
if (tryLock(key)) {
try {
return supplier.getAsBoolean();
} finally {
unlock(key);
}
}
return failResult;
}
public int tryLockExecute(K key, int failResult, IntSupplier supplier) {
if (tryLock(key)) {
try {
return supplier.getAsInt();
} finally {
unlock(key);
}
}
return failResult;
}
public long tryLockExecute(K key, long failResult, LongSupplier supplier) {
if (tryLock(key)) {
try {
return supplier.getAsLong();
} finally {
unlock(key);
}
}
return failResult;
}
public double tryLockExecute(K key, double failResult, DoubleSupplier supplier) {
if (tryLock(key)) {
try {
return supplier.getAsDouble();
} finally {
unlock(key);
}
}
return failResult;
}
public <T> T tryLockExecute(K key, T failResult, Supplier<T> supplier) {
if (tryLock(key)) {
try {
return supplier.get();
} finally {
unlock(key);
}
}
return failResult;
}
public boolean tryLockExecute(K key, BooleanSupplier failSupplier, BooleanSupplier supplier) {
if (tryLock(key)) {
try {
return supplier.getAsBoolean();
} finally {
unlock(key);
}
}
return failSupplier.getAsBoolean();
}
public int tryLockExecute(K key, IntSupplier failSupplier, IntSupplier supplier) {
if (tryLock(key)) {
try {
return supplier.getAsInt();
} finally {
unlock(key);
}
}
return failSupplier.getAsInt();
}
public long tryLockExecute(K key, LongSupplier failSupplier, LongSupplier supplier) {
if (tryLock(key)) {
try {
return supplier.getAsLong();
} finally {
unlock(key);
}
}
return failSupplier.getAsLong();
}
public double tryLockExecute(K key, DoubleSupplier failSupplier, DoubleSupplier supplier) {
if (tryLock(key)) {
try {
return supplier.getAsDouble();
} finally {
unlock(key);
}
}
return failSupplier.getAsDouble();
}
public <T> T tryLockExecute(K key, Supplier<T> failSupplier, Supplier<T> supplier) {
if (tryLock(key)) {
try {
return supplier.get();
} finally {
unlock(key);
}
}
return failSupplier.get();
}
public boolean tryLockExecute(K key, long timeout, Runnable runnable) {
if (tryLock(key, timeout, TimeUnit.MILLISECONDS)) {
try {
runnable.run();
return true;
} finally {
unlock(key);
}
}
return false;
}
public boolean tryLockExecute(K key, long timeout, boolean failResult, BooleanSupplier supplier) {
if (tryLock(key, timeout, TimeUnit.MILLISECONDS)) {
try {
return supplier.getAsBoolean();
} finally {
unlock(key);
}
}
return failResult;
}
public int tryLockExecute(K key, long timeout, int failResult, IntSupplier supplier) {
if (tryLock(key, timeout, TimeUnit.MILLISECONDS)) {
try {
return supplier.getAsInt();
} finally {
unlock(key);
}
}
return failResult;
}
public long tryLockExecute(K key, long timeout, long failResult, LongSupplier supplier) {
if (tryLock(key, timeout, TimeUnit.MILLISECONDS)) {
try {
return supplier.getAsLong();
} finally {
unlock(key);
}
}
return failResult;
}
public double tryLockExecute(K key, long timeout, double failResult, DoubleSupplier supplier) {
if (tryLock(key, timeout, TimeUnit.MILLISECONDS)) {
try {
return supplier.getAsDouble();
} finally {
unlock(key);
}
}
return failResult;
}
public <T> T tryLockExecute(K key, long timeout, T failResult, Supplier<T> supplier) {
if (tryLock(key, timeout, TimeUnit.MILLISECONDS)) {
try {
return supplier.get();
} finally {
unlock(key);
}
}
return failResult;
}
public boolean tryLockExecute(K key, long timeout, BooleanSupplier failSupplier, BooleanSupplier supplier) {
if (tryLock(key, timeout, TimeUnit.MILLISECONDS)) {
try {
return supplier.getAsBoolean();
} finally {
unlock(key);
}
}
return failSupplier.getAsBoolean();
}
public int tryLockExecute(K key, long timeout, IntSupplier failSupplier, IntSupplier supplier) {
if (tryLock(key, timeout, TimeUnit.MILLISECONDS)) {
try {
return supplier.getAsInt();
} finally {
unlock(key);
}
}
return failSupplier.getAsInt();
}
public long tryLockExecute(K key, long timeout, LongSupplier failSupplier, LongSupplier supplier) {
if (tryLock(key, timeout, TimeUnit.MILLISECONDS)) {
try {
return supplier.getAsLong();
} finally {
unlock(key);
}
}
return failSupplier.getAsLong();
}
public double tryLockExecute(K key, long timeout, DoubleSupplier failSupplier, DoubleSupplier supplier) {
if (tryLock(key, timeout, TimeUnit.MILLISECONDS)) {
try {
return supplier.getAsDouble();
} finally {
unlock(key);
}
}
return failSupplier.getAsDouble();
}
public <T> T tryLockExecute(K key, long timeout, Supplier<T> failSupplier, Supplier<T> supplier) {
if (tryLock(key, timeout, TimeUnit.MILLISECONDS)) {
try {
return supplier.get();
} finally {
unlock(key);
}
}
return failSupplier.get();
}
public boolean tryLockExecute(K key, long timeout, TimeUnit unit, Runnable runnable) {
if (tryLock(key, timeout, unit)) {
try {
runnable.run();
return true;
} finally {
unlock(key);
}
}
return false;
}
public boolean tryLockExecute(K key, long timeout, TimeUnit unit, boolean failResult, BooleanSupplier supplier) {
if (tryLock(key, timeout, unit)) {
try {
return supplier.getAsBoolean();
} finally {
unlock(key);
}
}
return failResult;
}
public int tryLockExecute(K key, long timeout, TimeUnit unit, int failResult, IntSupplier supplier) {
if (tryLock(key, timeout, unit)) {
try {
return supplier.getAsInt();
} finally {
unlock(key);
}
}
return failResult;
}
public long tryLockExecute(K key, long timeout, TimeUnit unit, long failResult, LongSupplier supplier) {
if (tryLock(key, timeout, unit)) {
try {
return supplier.getAsLong();
} finally {
unlock(key);
}
}
return failResult;
}
public double tryLockExecute(K key, long timeout, TimeUnit unit, double failResult, DoubleSupplier supplier) {
if (tryLock(key, timeout, unit)) {
try {
return supplier.getAsDouble();
} finally {
unlock(key);
}
}
return failResult;
}
public <T> T tryLockExecute(K key, long timeout, TimeUnit unit, T failResult, Supplier<T> supplier) {
if (tryLock(key, timeout, unit)) {
try {
return supplier.get();
} finally {
unlock(key);
}
}
return failResult;
}
public boolean tryLockExecute(K key, long timeout, TimeUnit unit, BooleanSupplier failSupplier, BooleanSupplier supplier) {
if (tryLock(key, timeout, unit)) {
try {
return supplier.getAsBoolean();
} finally {
unlock(key);
}
}
return failSupplier.getAsBoolean();
}
public int tryLockExecute(K key, long timeout, TimeUnit unit, IntSupplier failSupplier, IntSupplier supplier) {
if (tryLock(key, timeout, unit)) {
try {
return supplier.getAsInt();
} finally {
unlock(key);
}
}
return failSupplier.getAsInt();
}
public long tryLockExecute(K key, long timeout, TimeUnit unit, LongSupplier failSupplier, LongSupplier supplier) {
if (tryLock(key, timeout, unit)) {
try {
return supplier.getAsLong();
} finally {
unlock(key);
}
}
return failSupplier.getAsLong();
}
public double tryLockExecute(K key, long timeout, TimeUnit unit, DoubleSupplier failSupplier, DoubleSupplier supplier) {
if (tryLock(key, timeout, unit)) {
try {
return supplier.getAsDouble();
} finally {
unlock(key);
}
}
return failSupplier.getAsDouble();
}
public <T> T tryLockExecute(K key, long timeout, TimeUnit unit, Supplier<T> failSupplier, Supplier<T> supplier) {
if (tryLock(key, timeout, unit)) {
try {
return supplier.get();
} finally {
unlock(key);
}
}
return failSupplier.get();
}
static final class Lock extends ReentrantLock {
/**
* 正在使用或者是准备使用该锁的数量
*/
int using;
/**
* 锁的有参构造函数
*
* @param fair 是否公平
*/
Lock(boolean fair) {
super(fair);
}
}
}
都看到这了 点赞+关注一波咯