java - 锁粒度

最近工作有个需求,需要加锁保证操作的原子性,但在一定程度上我想着可以根据业务类型对锁进行细化,于是简单的写了一个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,以上方案在分布式下并不可用


版权声明:本文为littlewhitevg原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。