# 开发中锁的简单实现

开发中锁的简单实现

  • 业务需求场景:多个用户共同抢票,多个线程共同使用某个资源。
  • 问题分析:如果多个线程对某个共有资源进行操作的时候,如果不对资源进行上锁,那么就会出现超卖,线程之间不同步的问题,处理这个问题有多种方式。

乐观锁

CAS(Compare-and-Swap)比较并替换。读到某个值,对它进行更新之前,检查原值是否变化了,如果发生变化那么将原值更新为变化后的值。这两步骤是原子性的。乐观锁不是锁,而是一个在循环里尝试CAS的算法。

​ Juc下的原子相关的操作是乐观锁,

悲观锁

​ 悲观锁认为竞争总是会发生,因此每次对某资源进行操作时,都会持有一个独占的 锁。例如synchronizedLock

重入锁

​ 是以线程为单位,当一个线程获取对象锁之后,这个线程可以再 次获取本对象上的锁,而其他的线程是不可以的。

使用RedissonClient API实现分布式锁

redisson内部提供了监控锁的进程 ,可以通过设置key的有效时间进行防止死锁的问题,在加锁的时候应该考虑锁的颗粒度要小,范围尽量小。

  • 获得锁
public boolean tryGetLock(String lockKey, String requestId, int expireTime) {
    // 获取一把锁
    RLock lock = redissonClient.getLock(lockKey);
    try {
        // 加锁
        lock.lock();
        logger.info("=====> "+lockKey+"加锁成功");
        return true;
    } catch (Exception e) {
        logger.error("=====> 出错:"+e.getMessage());
    }
    return false;
}
  • 释放锁
public boolean tryRelaseLock(String lockKey){
    try {
        RLock lock = redissonClient.getLock(lockKey);
        lock.unlock();
        logger.info("=====> "+lockKey+"释放锁成功!");
        return true;
    }catch (Exception e){
        logger.info("=====> "+lockKey+"释放锁失败!");
        return false;
    }
}
  • 测试代码
@RequestMapping("/test4")
public String test4(@RequestParam String name) {
    try {
        boolean flag = tryGetLock("ticketnum", null, 10);
        if(!flag){
            return "当前资源正在被使用请排队...";
        }
        RBucket<Ticket> ticket = redissonClient.getBucket("ticket");
        Ticket tickets = ticket.get();
        Integer num = tickets.getNum();
        logger.info("=====> 当前总票数为:"+num);
        if(num > 0){
            try{
                Thread.sleep(3000);
            }catch (Exception e){
                logger.error(e.getMessage());
            }
            num = num-1;
            tickets.setNum(num);
            redissonClient.getBucket("ticket").set(tickets);
            logger.info("=====> 当前票的库存为:"+num);
        }else{
            logger.info("=====> 票已经抢完了...");
        }
        return "抢票操作完成";
    } catch (Exception e) {
        return e.getMessage();
    } finally {
        tryRelaseLock("ticketnum");
    }
}
JDK synchronized锁

​ 在加了它的方法、代码块中,一次只允许一个线程进入特定代码段,从而避免多线程同时修改同一数据。在synchronized代码语句块内操作时原子的,一个资源在一个时刻只能允许一个线程持有该资源,并且进行操作它。

synchronized加锁之后,其他获取该资源的线程会被阻塞。当代码退出或者抛出异常的时候,会释放锁对象。

  • 修饰方法的时候,当前对象作为锁的对象。

  • 修饰类的时候,默认是当前类的Class对象为锁的对象。

  • 测试代码:当有多个请求进来的时候,第一个线程请求得到锁的对象,别的线程都被阻塞了,等到第一个线程执行结束后,其他线程一次加锁执行。

@RequestMapping("/locktest")
public synchronized void test1() {
    for (int i = 0; i < 10; i++) {
        dealNum();
    }
    System.out.println("======>" + num1);
    System.out.println("======>" + num2);
    num1 = 0;
    num2 = 0;
}
JDK Lock的使用

Lock锁的释放需要显示的进行释放,如果不释放资源会发生死锁问题。

/**
  * 多个线程使用同一个锁
  */
private ReentrantLock lock = new ReentrantLock();

@Override
public void test11() {
    logger.info("doOne()执行前...");
    doOne();
    logger.info("doOne()执行后...");
}

private void doOne() {
    lock.lock();
    try {
        logger.info("doOne()执行中...");
        Thread.sleep(6000);
    } catch (Exception e) {

    } finally {
        lock.unlock();
    }
}
  • Lock接口的实现ReentrantLock锁,分为公平锁和非公平锁。公平锁,线程按照申请锁的顺序去执行,非公平锁会直接去尝试获取锁,如果获取不到那么会放入队列中。简单理解就是排队的时候插队和不插队的问题。
// 默认无参构造函数 非公平锁
public ReentrantLock() {
	sync = new NonfairSync();
}
// 公平锁
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值