Java实现对某个值加同步锁


适用场景

在一些场景下,我们需要对某一个值进行加锁,比如支付的订单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);
            } finally {
                lockMap.remove();
            }
            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 + "走了");
    }

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值