mybatis阻塞缓存BlockingCache源码分析

  1. 在分析之前我们先学习一个知识点CountDownLatch的使用
public static void main(String[] args) throws InterruptedException {
    CountDownLatch countDownLatch = new CountDownLatch(2);
    
    System.out.println("main start");
    new Thread(() -> {
      try {
        System.out.println("thread1 start");
        Thread.sleep(2000);
        System.out.println("thread1 end");
      } catch (InterruptedException e) {
        e.printStackTrace();
      } finally {
        if (countDownLatch != null) {
          countDownLatch.countDown();
        }
      }
    }).start();

    new Thread(() -> {
      try {
        System.out.println("thread2 start");
        Thread.sleep(3000);
        System.out.println("thread2 end");
      } catch (InterruptedException e) {
        e.printStackTrace();
      } finally {
        Optional.ofNullable(countDownLatch).ifPresent(CountDownLatch::countDown);
      }
    }).start();

    countDownLatch.await();
    System.out.println("main end");

  }

CountDownLatch的作用就是让主线程等待两个子线程执行结束再继续执行
几个关键点方法:

  1. new CountDownLatch(2)创建倒计时锁存器🔒,倒计时为2
  2. countDownLatch.await() 等待倒计时结束
  3. countDownLatch.countDown()计时器减一

BlockingCache关键方法源码解读:

public class BlockingCache implements Cache {

  private long timeout;
  private final Cache delegate;
  // 一条sql查询语句对应一个倒计时锁存器,使用ConcurrentHashMap保证了线程的安全
  private final ConcurrentHashMap<Object, CountDownLatch> locks;

  public BlockingCache(Cache delegate) {
    this.delegate = delegate;
    this.locks = new ConcurrentHashMap<>();
  }
  @Override
  public void putObject(Object key, Object value) {
    try {
      delegate.putObject(key, value);
    } finally {
      // 线程没有到缓存获取到数据,从数据库获取到之后加入缓存,但是刚才获取的倒计时锁依然没有释放
      // 所以这里还要释放倒计时锁
      releaseLock(key);
    }
  }

  @Override
  public Object getObject(Object key) {
    acquireLock(key);
    Object value = delegate.getObject(key);
    if (value != null) {
      // 如果从缓存中获取到了值就释放倒计时锁
      releaseLock(key);
    }
    return value;
  }

  /**
   * 保证了同一时刻只有一个线程能够访问
   * @param key
   */
  private void acquireLock(Object key) {
    CountDownLatch newLatch = new CountDownLatch(1);
    // 感觉这里还是没有考虑周全,容易造成死循环
    while (true) {
      System.out.println(Thread.currentThread().getName() + ":get");
      // 如果没有获取到倒计时器,说明put到map成功,获取锁成功
      CountDownLatch latch = locks.putIfAbsent(key, newLatch);
      if (latch == null) {
        System.out.println(Thread.currentThread().getName() + "获取到了锁==========》" + newLatch);
        break;
      }
      try {
        // todo 其实这里的超时时间没有用的,因为没有找到设置超时时间的入口
        if (timeout > 0) {
          // 如果获取到latch,说明已经有线程正在获取,执行超时等待
          boolean acquired = latch.await(timeout, TimeUnit.MILLISECONDS);
          if (!acquired) {
            throw new CacheException(
                "Couldn't get a lock in " + timeout + " for the key " + key + " at the cache " + delegate.getId());
          }
        } else {
          latch.await();
        }
      } catch (InterruptedException e) {
        throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e);
      }
    }
  }

  private void releaseLock(Object key) {
    CountDownLatch latch = locks.remove(key);
    if (latch == null) {
      throw new IllegalStateException("Detected an attempt at releasing unacquired lock. This should never happen.");
    }
    latch.countDown();
    System.out.println(Thread.currentThread().getName() + "释放锁");
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值