返回顶部
首页 > 资讯 > 精选 >Java根据某个key加锁怎么实现
  • 844
分享到

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

2023-07-05 12:07:47 844人浏览 安东尼
摘要

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

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

    一、背景

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

    二、参考代码

    接口定义

    public interface LockByKey<T> {        void lock(T key);        void unlock(T key);}

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

    2.1.1 代码实现

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

    import java.util.Map;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.locks.ReentrantLock;public class DefaultLockByKeyImpl<T> implements LockByKey<T> {    private final Map<T, ReentrantLock> lockMap = new ConcurrentHashMap<>();        @Override    public void lock(T key) {        // 如果key为空,直接返回        if (key == null) {            throw new IllegalArgumentException("key 不能为空");        }                // 获取或创建一个ReentrantLock对象        ReentrantLock lock = lockMap.computeIfAbsent(key, k -> new ReentrantLock());        // 获取锁        lock.lock();    }        @Override    public void unlock(T key) {        // 如果key为空,直接返回        if (key == null) {            throw new IllegalArgumentException("key 不能为空");        }        // 从Map中获取锁对象        ReentrantLock lock = lockMap.get(key);        // 获取不到报错        if (lock == null) {            throw new IllegalArgumentException("key " + key + "尚未加锁");        }        // 其他线程非法持有不允许释放        if (!lock.isHeldByCurrentThread()) {            throw new IllegalStateException("当前线程尚未持有,key:" + key + "的锁,不允许释放");        }        lock.unlock();    }}

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

    2.1.2 编写单测
    import com.Google.common.collect.Lists;import org.junit.Test;import java.util.HashSet;import java.util.List;import java.util.Set;import java.util.concurrent.CountDownLatch;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit;public class DefaultLockByKeyImplTest {    private final LockByKey<String> lockByKey = new DefaultLockByKeyImpl<>();    private final CountDownLatch countDownLatch = new CountDownLatch(7);    private final ExecutorService executorService = Executors.newFixedThreadPool(10);    @Test    public void test() throws InterruptedException {        List<String> keys = Lists.newArrayList("a", "a", "a", "b", "c", "b", "d");        Set<String> executingKeySet = new HashSet<>();        for (int i = 0; i < keys.size(); i++) {            String key = keys.get(i);            int finalI = i;            executorService.submit(() -> {                lockByKey.lock(key);                if (executingKeySet.contains(key)) {                    throw new RuntimeException("存在正在执行的 key:" + key);                }                executingKeySet.add(key);                try {                    System.out.println("index:" + finalI + "对 [" + key + "] 加锁 ->" + Thread.currentThread().getName());                    TimeUnit.SECONDS.sleep(1);                } catch (InterruptedException e) {                    throw new RuntimeException(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 控制同时执行的线程数。

    import lombok.SneakyThrows;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.Semaphore;public class SimultaneousEntriesLockByKey<T> implements LockByKey<T> {    private final Map<T, Semaphore> semaphores = new ConcurrentHashMap<>();        private int allowed_threads;    public SimultaneousEntriesLockByKey(int allowed_threads) {        this.allowed_threads = allowed_threads;    }        @Override    public void lock(T key) {        Semaphore semaphore = semaphores.compute(key, (k, v) -> v == null ? new Semaphore(allowed_threads) : v);        semaphore.acquireUninterruptibly();    }        @Override    public void unlock(T key) {        // 如果key为空,直接返回        if (key == null) {            throw new IllegalArgumentException("key 不能为空");        }        // 从Map中获取锁对象        Semaphore semaphore = semaphores.get(key);        if (semaphore == null) {            throw new IllegalArgumentException("key " + key + "尚未加锁");        }        semaphore.release();        if (semaphore.availablePermits() >= allowed_threads) {            semaphores.remove(key, semaphore);        }    }
    2.2.2 测试代码
    import com.google.common.collect.Lists;import org.junit.Test;import java.time.LocalDateTime;import java.util.Collections;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.concurrent.CountDownLatch;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit;public class SimultaneousEntriesLockByKeyTest {    private final int maxThreadEachKey = 2;    private final LockByKey<String> lockByKey = new SimultaneousEntriesLockByKey<>(maxThreadEachKey);    private final CountDownLatch countDownLatch = new CountDownLatch(7);    private final ExecutorService executorService = Executors.newFixedThreadPool(10);    @Test    public void test() throws InterruptedException {        List<String> keys = Lists.newArrayList("a", "a", "a", "b", "c", "b", "d");        Map<String, Integer> executingKeyCount = Collections.synchronizedMap(new HashMap<>());        for (int i = 0; i < keys.size(); i++) {            String key = keys.get(i);            int finalI = i;            executorService.submit(() -> {                lockByKey.lock(key);                executingKeyCount.compute(key, (k, v) -> {                    if (v != null && v + 1 > maxThreadEachKey) {                        throw new RuntimeException("超过限制了");                    }                    return v == 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 (InterruptedException e) {                    throw new RuntimeException(e);                } finally {                    System.out.println("time:" + LocalDateTime.now().toString() + " ,index:" + finalI + "释放 [" + key + "] ->" + Thread.currentThread().getName() + "count:" + (executingKeyCount.get(key) - 1));                    lockByKey.unlock(key);                    executingKeyCount.compute(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加锁怎么实现”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

    --结束END--

    本文标题: Java根据某个key加锁怎么实现

    本文链接: https://lsjlt.com/news/351870.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    猜你喜欢
    • Java根据某个key加锁怎么实现
      本篇内容主要讲解“Java根据某个key加锁怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java根据某个key加锁怎么实现”吧!一、背景日常开发中,有时候需要根据某个 key 加锁,确...
      99+
      2023-07-05
    • Java根据某个key加锁的实现方式
      目录一、背景二、参考代码2.1 同一个 key 只能一个线程执行2.1.1 代码实现2.1.2 编写单测2.2、同一个 key 可以有 n个线程执行2.2.1 代码实现2.2.2 测...
      99+
      2023-03-19
      Java根据某个 key 加锁 Java 加锁
    • php二维数组根据某个字段排序怎么实现
      可以使用usort()函数来实现二维数组根据某个字段排序。 下面是一个示例代码: $students = array( ar...
      99+
      2023-10-27
      php
    • oracle怎么根据某个字段去重
      可以使用Oracle中的DISTINCT关键字来根据某个字段进行去重。具体的用法是在SELECT语句中使用DISTINCT关...
      99+
      2023-08-15
      oracle
    • java怎么判断JSONObject是否存在某个Key
      在Java中,你可以使用JSONObject的`has`方法来判断一个JSONObject对象是否存在某个key。`has`方法接受...
      99+
      2023-08-16
      java
    • mysql中怎么给某行数据加锁
      在MySQL中,可以使用SELECT … FOR UPDATE语句来给某行数据加锁。当使用SELECT … FOR UPDA...
      99+
      2024-04-17
      mysql
    • java中怎么判断JSONObject是否存在某个Key
      本文小编为大家详细介绍“java中怎么判断JSONObject是否存在某个Key”,内容详细,步骤清晰,细节处理妥当,希望这篇“java中怎么判断JSONObject是否存在某个Key”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一...
      99+
      2023-07-02
    • Java怎么根据key值修改Hashmap中的value值
      这篇文章主要讲解了“Java怎么根据key值修改Hashmap中的value值”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java怎么根据key值修改Hashmap中的value值”吧!根...
      99+
      2023-07-05
    • java map根据值排序怎么实现
      要根据Map的值进行排序,可以使用Java 8中的Stream和Lambda表达式来实现。下面是一个示例代码: import jav...
      99+
      2023-10-21
      java
    • redis怎么实现加锁和解锁
      这篇文章运用简单易懂的例子给大家介绍redis怎么实现加锁和解锁,代码非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。redis是没有锁机制的哟,对于多个用户连接也不存在竞争问题。但是在进行并...
      99+
      2024-04-02
    • java定时执行某个方法怎么实现
      在Java中,可以使用Timer和TimerTask类来实现定时执行某个方法。首先,创建一个继承自TimerTask的类,重写run...
      99+
      2023-10-20
      java
    • 利用Java怎么实现一个排他锁
      利用Java怎么实现一个排他锁?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。一 .前言某年某月某天,同事说需要一个文件排他锁功能,需求如下:(1)写操作是排他属...
      99+
      2023-05-31
      java 排他锁 ava
    • php怎么根据键值去除数组中的某个元素
      php根据键值去除数组中元素的方法:1、使用array_search()函数在数组中搜索指定键值,并返回对应的键名,语法“array_search(指定键名,$arr,true);”;2、使用unset()函数根据获取的键名来删除指定数组元...
      99+
      2022-06-14
      php php数组
    • HashMap怎么实现保存两个key相同的数据
      这篇文章主要讲解了“HashMap怎么实现保存两个key相同的数据”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“HashMap怎么实现保存两个key相同的数据”吧!HashMap如何保存两个...
      99+
      2023-06-20
    • java怎么获取list中的某个数据
      要从一个List中获取特定的数据,你可以使用get()方法。这个方法接受一个索引作为参数,并返回该索引对应的元素。以下是一个示例代码...
      99+
      2023-08-15
      java list
    • nodejs怎么实现某个函数线
      这篇文章主要介绍“nodejs怎么实现某个函数线”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“nodejs怎么实现某个函数线”文章能帮助大家解决问题。什么是函数线函数线是一条由多个函数组成的线性执行...
      99+
      2023-07-05
    • Java开发HashMap key怎么实现hashCode equals
      本篇内容主要讲解“Java开发HashMap key怎么实现hashCode equals”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java开发HashMap ...
      99+
      2023-07-05
    • Redis怎么加锁实现高并发
      在Redis中可以使用SETNX命令实现简单的分布式锁。SETNX命令是一个原子操作,用于设置一个键的值,如果该键不存在,则设置成功...
      99+
      2023-08-31
      Redis
    • Java怎么实现根据年龄范围输出年龄段
      今天小编给大家分享一下Java怎么实现根据年龄范围输出年龄段的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一、根据年龄范围输...
      99+
      2023-06-27
    • 怎么在java项目中添加一个文件锁
      今天就跟大家聊聊有关怎么在java项目中添加一个文件锁,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。java  文件锁的 实例代码:import jav...
      99+
      2023-05-31
      java 文件锁 ava
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作