java 线程安全问题(库存超卖问题)(不用锁、CAS锁、同步锁synchronized、重入锁ReentrantLock)

本文探讨了线程不安全问题,介绍了CAS锁、synchronized、ReentrantLock在Java中的应用,强调了单节点服务中的解决方案,并指出在多节点服务中需考虑分布式事务和分布式锁,如Seata和Redisson。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里记录一下线程安全以及几种锁的示例

1.线程不安全的示例(线程不安全):

    private Integer store = 10000;

    /**
     * @Description: 线程不安全
     * @Param: []
     * @Author: tom
     * @Date: 2023/12/22
     * @Return int
     */
    public int notSafe() {
        store--;
        return store;
    }
    @Test
    public void notSafeTest() throws InterruptedException {
        for (int i = 1; i <= 10000; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName()+"_线程不安全扣减后:"+notSafe());
            }).start();
        }
        Thread.sleep(5000);
        System.out.println("最终:"+store);
    }

库存1W然后,启用1W个线程进行扣减,结果居然有剩余,每次执行结果不一样,反正就是不为0,这时候老板要扣你工资了!!!

2.解决的方法当然有

a.使用CAS锁;

private AtomicInteger store1 = new AtomicInteger(10000);
    /**
     * @Description: 线程安全
     * @Param: []
     * @Author: tom
     * @Date: 2023/12/22
     * @Return int
     */
    public int casLock() {
        int oldValue = store1.get();
        int newValue = oldValue - 1;
        while (!store1.compareAndSet(oldValue, newValue)) {
            // 如果CAS失败,则重新读取当前值并尝试再次CAS
            oldValue = store1.get();
            newValue = oldValue - 1;
        }

        return newValue;
    }
    @Test
    public void casLockTeset() throws InterruptedException {
        for (int i = 1; i <= 10000; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName()+"_cas扣减后:"+casLock());
            }).start();

        }
        Thread.sleep(5000);
        System.out.println("最终:"+store1);
    }

执行结果:

最终结果为0了,你又可以继续摸鱼了。

b.使用同步锁synchronized:

    private Integer store2 = 10000;
    /**
     * @Description: synchronizedLock锁
     * @Param: []
     * @Author: tom
     * @Date: 2023/12/22
     * @Return int
     */
    public synchronized int synchronizedLock() {
        store2--;
        return store2;
    }
    @Test
    public void synchronizedLockTest() throws InterruptedException {
        for (int i = 1; i <= 10000; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName()+"_synchronized扣减后:"+synchronizedLock());
            }).start();
        }
        Thread.sleep(5000);
        System.out.println("最终:"+store2);
    }

执行结果:

结果为0,你又可以快乐地刷抖音了。

c.使用重入锁ReentrantLock

private final ReentrantLock lock = new ReentrantLock();
    private int count = 10000;

    public int reentrantLockCount() {
        lock.lock();  // 获取锁
        try {
            count--;
        } finally {
            lock.unlock();  // 释放锁
        }
        return count;
    }



    @Test
    public void reentrantLockTest() throws InterruptedException {
        for (int i = 1; i <= 10000; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName()+"_count:"+reentrantLockCount());
            }).start();
        }
        Thread.sleep(5000);
        System.out.println("最终:"+count);
    }

运行结果:

最终结果为0了,你又可以继续摸鱼了。

4.特别注意:

以上三种方法只是适用于单节点服务,如果系统是多节点服务,以上做法就不能保证线程安全了!

需要引入分布式事务(如Alibaba 的 Seata)和分布式锁(如Redisson)。

分布式事务和分布式锁示例:后续补充......

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值