Java并发编程 ReentrantLock可重入锁 - 防止缓存穿透

ReentrantLock是一个可重入且独占式的锁,它具有与使用synchronized监视器锁相同的基本行为和语义,但与synchronized关键字相比,它更灵活、更强大,增加了轮询、超时、中断等高级功能。

参考:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReentrantLock.html

可重入性

当一个线程成功加锁且没有释放的时候,这个线程就是这个锁的所有者。当锁没有被任何线程拥有,调用lock方法的时候会成功占用该锁。如果当前线程已经是锁的所有者,调用lock方法将立即返回。可以使用isheldbycurrentthread()和getholdcount()方法检查线程对锁的占用情况。

ReentrantLock的构造函数接收一个boolean的参数,如果设置为true,等待时间最长的线程将会获得锁,否则不保证获得锁的特定顺序。

一个简单例子:

class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m() {
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }

应用场景一:防止缓存穿透

当某个线程占用了锁,其他线程执行到lock就会被阻塞,只有这个线程释放后才继续。

在占用锁的时候,需要二次确认缓存,避免被阻塞的线程都穿透都慢数据源。

public void method1() throws InterruptedException {
    //占用锁
    lock.lock();
    try {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " start load L1 cache.");
        //要执行缓存二次确认,从缓存取数据,避免又查慢数据源
        //这里模拟穿透到慢数据源
        Thread.sleep(5000);
        //刷入缓存
        System.out.println(threadName+ " end load L1 cache.");
    } finally {
        //即使遇到异常,最终也会释放锁
        lock.unlock();
    }
}

以上的例子在lock.lock会阻塞线程,在回源的时候会产生响应时间尖峰,可以采用二级缓存避免这个尖峰。

这里调用了tryLock方法,该方法会立刻返回,而不是一直阻塞

public void method1() throws InterruptedException {
    String threadName = Thread.currentThread().getName();
    //占用锁
    if (lock.tryLock()) {
        //仅仅有该线程可以会源
        try {
            System.out.println(threadName + " start load L1 cache.");
            //要执行缓存二次确认,从缓存取数据,避免又查慢数据源
            //这里模拟穿透到慢数据源
            Thread.sleep(5000);
            System.out.println(threadName+ " end load L1 cache.");
        } finally {
            //即使遇到异常,最终也会释放锁
            lock.unlock();
        }
    } else {
        //读取二级缓存,当然,二级缓存的有效时间必须比1级缓存的要长,避免二级缓存无效
        System.out.println(threadName + " load L2 cache.");
    }
}

 

你可能感兴趣的:(Java,java,ReentrantLock)