最近工作有个需求,需要加锁保证操作的原子性,但在一定程度上我想着可以根据业务类型对锁进行细化,于是简单的写了一个demo进行验证。
先来看看synchronized的demo:
import java.util.concurrent.ConcurrentHashMap;
public class MapLockDemo {
private static final ConcurrentHashMap<String, Object> lockMap = new ConcurrentHashMap<>();
public static void main(String[] args) {
lockMap.put("key_1", new Object());
new Thread(() -> {
try {
syncLock1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
syncLock2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
public static void syncLock1() throws InterruptedException {
Object key1 = lockMap.get("key_1");
System.out.println("syncLock1获取中");
synchronized (key1) {
System.out.println("syncLock1已获取");
Thread.sleep(10000);
}
System.out.println("syncLock1已经释放");
}
public static void syncLock2() throws InterruptedException {
Object key1 = lockMap.get("key_1");
System.out.println("syncLock2获取中");
synchronized (key1) {
System.out.println("syncLock2已获取");
Thread.sleep(10000);
}
System.out.println("syncLock2已经释放");
}
}
结果如下:
那么我们就可以维护一个锁池来进行细粒度的控制:
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class RepertoryService {
// key -> lockObject
private final ConcurrentHashMap<String, Object> lockMap = new ConcurrentHashMap<>();
//分段减的库存
private final Map<String, Integer> quotaMap = new HashMap<>();
public synchronized Object getLock(String key) {
Object lock = lockMap.get(key);
if (lock == null) {
Object lockObject = new Object();
lockMap.put(key, lockObject);
return lockObject;
} else {
return lock;
}
}
public void addQuota(String key, int number) throws InterruptedException {
Object lock = getLock(key);
synchronized (lock) {
Thread.sleep(100);
// System.out.println(String.format("addQuota:{%s},key:{%s}", number, key));
Integer quota = quotaMap.get(key);
quota += number;
quotaMap.put(key, quota);
System.out.println(String.format("key -> {%s},now quota:{%s}", key, quota));
}
}
public void addQuotaNoSync(String key, int number) throws InterruptedException {
Thread.sleep(100);
// System.out.println(String.format("addQuota:{%s},key:{%s}", number, key));
Integer quota = quotaMap.get(key);
quota += number;
quotaMap.put(key, quota);
System.out.println(String.format("key -> {%s},now quota:{%s}", key, quota));
}
public void subQuota(String key, int number) throws InterruptedException {
Object lock = getLock(key);
synchronized (lock) {
Thread.sleep(100);
// System.out.println(String.format("addQuota:{%s},key:{%s}", number, key));
Integer quota = quotaMap.get(key);
quota -= number;
quotaMap.put(key, quota);
System.out.println(String.format("key -> {%s},now quota:{%s}", key, quota));
}
}
public static void main(String[] args) {
RepertoryService service = new RepertoryService();
ExecutorService pool = Executors.newFixedThreadPool(8);
for (int i = 0; i < 100; i++) {
service.quotaMap.put(generateKey(i), 100);
}
for (int i = 0; i < 100; i++) {
int finnalI = i;
pool.execute(() -> {
try {
service.addQuotaNoSync(generateKey(finnalI), 1);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
pool.shutdown();
System.out.println(service.quotaMap.toString());
}
public static String generateKey(int n) {
return "quota_" + n % 10;
}
}
对与不同key值获取不同的锁,这个key值可有业务模型进行生成。这里采用了一个简单的object作为synchronized的锁对象。其实也可以采用java自带的一些锁来作为lockMap的value部分。但在这里为了保证获取锁的线程安全,避免相同的key在同时获取锁的时候创建了多个不一样的锁,做了一个全局的获取锁的锁来保证线程安全。
其实再优化一下及对业务量有个预估,将这些锁预先创建出来,通过对key的hash值来hash到这些预先创建的锁上。那么就不用一个全局的获取锁的锁来保证锁的创建了。
但是如今java项目基本都是微服务架构,基本等同于废弃了synchronized和自带的Lock,以上方案在分布式下并不可用