Java实现对某个值加同步锁

阅读 83

2022-02-11

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


适用场景

在一些场景下,我们需要对某一个值进行加锁,比如支付的订单id,同一时间一个订单只允许一个线程进行操作。


以下是本篇文章正文内容,下面案例可供参考

一、synchronized(this)

	@GetMapping("thread")
    public void thread(String id) {
        synchronized (this) {
            System.out.println(id + "进来了");
            try {
                //三秒后
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(id + "走了");
        }
    }

示例:通过synchronized(this)进行加锁,可以实现同一时间只能有一个线程进入到代码中来,实现了对id的锁,但是有一个问题,不同的id都不能被同时执行,肯定是不满足高并发场景的。

1进来了
1走了
2进来了
2走了

二、synchronized(Object)

把synchronized(this)修改为synchronized(id.intern())就可以实现对某一个值进行加锁了(注意这里需要使用Object.intern(),不然每个线程的id都是不同的对象)

1进来了
2进来了
1走了
2走了

这样就可以实现每个多个线程同时执行了,但是因为默认string.intern是jni调用了c++接口,常量池是类似于hashmap的存在,默认大小是1009,所以当数据量越大,hash冲突越严重,链表越长,所以也不推荐这种写法

三、借用对象加锁

代码如下(示例):

	private static final Map<String, Object> lockMap = new ConcurrentHashMap<>();
    @GetMapping("thread")
    public void thread(String id) {
        Object o = lockMap.computeIfAbsent(id, k -> new Object());
        synchronized (o) {
            System.out.println(id + "进来了");
            try {
                //执行业务
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(id + "走了");
        }
    }

经验证后发现,当并发小于等于200时没问题,大于200时就有几率出现问题,原因是Spring默认的线程数量最大是200,超过200就会先处理200,完成一个再放一个进来,新放进来的会创建一个新的锁对象,导致并发执行。

在这里插入图片描述

四、最终版

private static Map<String, ReentrantLock> lockMap = new ConcurrentHashMap<>();
    @GetMapping("thread")
    public void thread(String id) {
        ReentrantLock o = null;
        do {
        	//开始执行先unlock
            if (o != null) {
                o.unlock();
            }
            o = lockMap.computeIfAbsent(id, k -> new ReentrantLock());
            //加锁
            o.lock();
            //新创建的被上一个线程remove掉了,或者新创建的对象和lockMap中已有的不是同一个对象,重试
        } while (lockMap.get(id) == null || o != lockMap.get(id));
        System.out.println(id + "进来了");
        try {
            //执行业务
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放锁
            lockMap.remove(id);
        }
        System.out.println(id + "走了");
    }

精彩评论(0)

0 0 举报