Java根据某个key加锁怎么实现


本篇内容主要讲解“Java根据某个key加锁怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java根据某个key加锁怎么实现”吧!

    一、背景

    日常开发中,有时候需要根据某个 key 加锁,确保多线程情况下,对该 key 的加锁和解锁之间的代码串行执行。
    大家可以借助每个 key 对应一个 ReentrantLock ,让同一个 key 的线程使用该 lock 加锁;每个 key 对应一个 Semaphore ,让同一个 key 的线程使用 Semaphore 控制同时执行的线程数。

    二、参考代码

    接口定义

    publicinterfaceLockByKey<T>{/***加锁*/voidlock(Tkey);/***解锁*/voidunlock(Tkey);}

    2.1 同一个 key 只能一个线程执行

    2.1.1 代码实现

    每个 key 对应一个 ReentrantLock ,让同一个 key 的线程使用该 lock 加锁。

    importjava.util.Map;importjava.util.concurrent.ConcurrentHashMap;importjava.util.concurrent.locks.ReentrantLock;publicclassDefaultLockByKeyImpl<T>implementsLockByKey<T>{privatefinalMap<T,ReentrantLock>lockMap=newConcurrentHashMap<>();/***加锁*/@Overridepublicvoidlock(Tkey){//如果key为空,直接返回if(key==null){thrownewIllegalArgumentException("key不能为空");}//获取或创建一个ReentrantLock对象ReentrantLocklock=lockMapputeIfAbsent(key,k->newReentrantLock());//获取锁lock.lock();}/***解锁*/@Overridepublicvoidunlock(Tkey){//如果key为空,直接返回if(key==null){thrownewIllegalArgumentException("key不能为空");}//从Map中获取锁对象ReentrantLocklock=lockMap.get(key);//获取不到报错if(lock==null){thrownewIllegalArgumentException("key"+key+"尚未加锁");}//其他线程非法持有不允许释放if(!lock.isHeldByCurrentThread()){thrownewIllegalStateException("当前线程尚未持有,key:"+key+"的锁,不允许释放");}lock.unlock();}}

    注意事项:
    (1)参数合法性校验
    (2)解锁时需要判断该锁是否为当前线程持有

    2.1.2 编写单测
    import&nbsp.googlemon.collect.Lists;importorg.junit.Test;importjava.util.HashSet;importjava.util.List;importjava.util.Set;importjava.util.concurrent.CountDownLatch;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.TimeUnit;publicclassDefaultLockByKeyImplTest{privatefinalLockByKey<String>lockByKey=newDefaultLockByKeyImpl<>();privatefinalCountDownLatchcountDownLatch=newCountDownLatch(7);privatefinalExecutorServiceexecutorService=Executors.newFixedThreadPool(10);@Testpublicvoidtest()throwsInterruptedException{List<String>keys=Lists.newArrayList("a","a","a","b","c","b","d");Set<String>executingKeySet=newHashSet<>();for(inti=0;i<keys.size();i++){Stringkey=keys.get(i);intfinalI=i;executorService.submit(()->{lockByKey.lock(key);if(executingKeySet.contains(key)){thrownewRuntimeException("存在正在执行的key:"+key);}executingKeySet.add(key);try{System.out.println("index:"+finalI+"对["+key+"]加锁->"+Thread.currentThread().getName());TimeUnit.SECONDS.sleep(1);}catch(InterruptedExceptione){thrownewRuntimeException(e);}finally{System.out.println("index:"+finalI+"释放["+key+"]->"+Thread.currentThread().getName());lockByKey.unlock(key);executingKeySet.remove(key);countDownLatch.countDown();}});}countDownLatch.await();}}

    如果同一个 key 没释放能够再次进入,会抛出异常。
    也可以通过日志来观察执行情况:

    index:0对[a]加锁->pool-1-thread-1index:6对[d]加锁->pool-1-thread-7index:4对[c]加锁->pool-1-thread-5index:3对[b]加锁->pool-1-thread-4index:6释放[d]->pool-1-thread-7index:4释放[c]->pool-1-thread-5index:0释放[a]->pool-1-thread-1index:3释放[b]->pool-1-thread-4index:1对[a]加锁->pool-1-thread-2index:5对[b]加锁->pool-1-thread-6index:1释放[a]->pool-1-thread-2index:5释放[b]->pool-1-thread-6index:2对[a]加锁->pool-1-thread-3index:2释放[a]->pool-1-thread-3

    2.2、同一个 key 可以有 n个线程执行

    2.2.1 代码实现

    每个 key 对应一个 Semaphore ,让同一个 key 的线程使用 Semaphore 控制同时执行的线程数。

    importlombok.SneakyThrows;importjava.util.Map;importjava.util.concurrent.ConcurrentHashMap;importjava.util.concurrent.Semaphore;publicclassSimultaneousEntriesLockByKey<T>implementsLockByKey<T>{privatefinalMap<T,Semaphore>semaphores=newConcurrentHashMap<>();/***最大线程*/privateintallowed_threads;publicSimultaneousEntriesLockByKey(intallowed_threads){this.allowed_threads=allowed_threads;}/***加锁*/@Overridepublicvoidlock(Tkey){Semaphoresemaphore=semaphorespute(key,(k,v)->v==null?newSemaphore(allowed_threads):v);semaphore.acquireUninterruptibly();}/***解锁*/@Overridepublicvoidunlock(Tkey){//如果key为空,直接返回if(key==null){thrownewIllegalArgumentException("key不能为空");}//从Map中获取锁对象Semaphoresemaphore=semaphores.get(key);if(semaphore==null){thrownewIllegalArgumentException("key"+key+"尚未加锁");}semaphore.release();if(semaphore.availablePermits()>=allowed_threads){semaphores.remove(key,semaphore);}}
    2.2.2 测试代码
    import&nbsp.googlemon.collect.Lists;importorg.junit.Test;importjava.time.LocalDateTime;importjava.util.Collections;importjava.util.HashMap;importjava.util.List;importjava.util.Map;importjava.util.concurrent.CountDownLatch;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.TimeUnit;publicclassSimultaneousEntriesLockByKeyTest{privatefinalintmaxThreadEachKey=2;privatefinalLockByKey<String>lockByKey=newSimultaneousEntriesLockByKey<>(maxThreadEachKey);privatefinalCountDownLatchcountDownLatch=newCountDownLatch(7);privatefinalExecutorServiceexecutorService=Executors.newFixedThreadPool(10);@Testpublicvoidtest()throwsInterruptedException{List<String>keys=Lists.newArrayList("a","a","a","b","c","b","d");Map<String,Integer>executingKeyCount=Collections.synchronizedMap(newHashMap<>());for(inti=0;i<keys.size();i++){Stringkey=keys.get(i);intfinalI=i;executorService.submit(()->{lockByKey.lock(key);executingKeyCountpute(key,(k,v)->{if(v!=null&&v+1>maxThreadEachKey){thrownewRuntimeException("超过限制了");}returnv==null?1:v+1;});try{System.out.println("time:"+LocalDateTime.now().toString()+",index:"+finalI+"对["+key+"]加锁->"+Thread.currentThread().getName()+"count:"+executingKeyCount.get(key));TimeUnit.SECONDS.sleep(1);}catch(InterruptedExceptione){thrownewRuntimeException(e);}finally{System.out.println("time:"+LocalDateTime.now().toString()+",index:"+finalI+"释放["+key+"]->"+Thread.currentThread().getName()+"count:"+(executingKeyCount.get(key)-1));lockByKey.unlock(key);executingKeyCountpute(key,(k,v)->v-1);countDownLatch.countDown();}});}countDownLatch.await();}}

    输出:

    time:2023-03-15T20:49:57.044195 ,index:6对 [d] 加锁 ->pool-1-thread-7count:1
    time:2023-03-15T20:49:57.058942 ,index:5对 [b] 加锁 ->pool-1-thread-6count:2
    time:2023-03-15T20:49:57.069789 ,index:1对 [a] 加锁 ->pool-1-thread-2count:2
    time:2023-03-15T20:49:57.042402 ,index:4对 [c] 加锁 ->pool-1-thread-5count:1
    time:2023-03-15T20:49:57.046866 ,index:0对 [a] 加锁 ->pool-1-thread-1count:2
    time:2023-03-15T20:49:57.042991 ,index:3对 [b] 加锁 ->pool-1-thread-4count:2
    time:2023-03-15T20:49:58.089557 ,index:0释放 [a] ->pool-1-thread-1count:1
    time:2023-03-15T20:49:58.082679 ,index:6释放 [d] ->pool-1-thread-7count:0
    time:2023-03-15T20:49:58.084579 ,index:4释放 [c] ->pool-1-thread-5count:0
    time:2023-03-15T20:49:58.083462 ,index:5释放 [b] ->pool-1-thread-6count:1
    time:2023-03-15T20:49:58.089576 ,index:3释放 [b] ->pool-1-thread-4count:1
    time:2023-03-15T20:49:58.085359 ,index:1释放 [a] ->pool-1-thread-2count:1
    time:2023-03-15T20:49:58.096912 ,index:2对 [a] 加锁 ->pool-1-thread-3count:1
    time:2023-03-15T20:49:59.099935 ,index:2释放 [a] ->pool-1-thread-3count:0

    到此,相信大家对“Java根据某个key加锁怎么实现”有了更深的了解,不妨来实际操作一番吧!这里是主机评测网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!


    上一篇:微信小程序Echarts动态使用及图表层级踩坑解决的方法

    下一篇:Qt怎么实现图片浏览器


    Copyright © 2002-2019 测速网 https://www.inhv.cn/ 皖ICP备2023010105号 城市 地区 街道
    温馨提示:部分文章图片数据来源与网络,仅供参考!版权归原作者所有,如有侵权请联系删除!
    热门搜索