并发编程系列之ReentrantLock用法简介

x33g5p2x  于2021-12-30 转载在 其他  
字(2.3k)|赞(0)|评价(0)|浏览(229)

并发编程系列之ReentrantLock用法简介

1、什么是ReentrantLock?

ReentrantLock是实现底层的Lock接口的可重入锁实现。支持公平锁模式和非公平锁模式。

典型例子:

ReentrantLocl rlock = new ReentrantLock();
try {
	rlock.lock();
	// 业务处理
} finally {
	rlock.unlock();
}

2、什么是重入锁和不可重入锁?

  1. 可重入锁:又称之为递归锁,也就是一个线程可以反复获取锁多次,一个线程获取到锁之后,内部如果还需要获取锁,可以直接再获取锁,前提是同一个对象或者class。ReentrantLock和synchronized都是可重入锁,可重入锁的最重要作用就是避免死锁的情况。
  2. 不可重入锁:又称之为自旋锁,实现可以是一个循环加上unsafe和cas机制,就是一直循环直到抢到锁,这个过程可以通过cas锁进行限制,如果一个线程获取到锁,cas (compareAndSet)会返回1,其它线程包括自己就不能再持有锁,需要等线程释放锁。

ReentrantLock是可重入锁,所以允许一个线程多次获取资源锁。第一次调用lock时,计数设置为1,再次获取资源锁时加1,调用unlock解锁,计数减1,直到减为0,释放锁资源,如图所示:

3、公平锁和非公平锁的区别?

  • 公平锁:多个线程按照申请锁的顺序去获取锁,线程会在队列里排队,按照顺序去获取锁。只有队列第1个线程才能获取到锁,获取到锁之后,其它线程都会阻塞等待,等到持有锁的线程释放锁,其它线程才会被唤醒。
  • 非公平锁:多个线程都会去竞争获取锁,获取不到就进入队列等待,竞争得到就直接获取锁;然后持有锁的线程释放锁之后,所有等待的线程就都会去竞争锁。
    synchronized是非公平锁,ReentrantLock可以支持非公平锁和公平锁,new ReentrantLock(true),加上个true就是公平锁,默认情况是非公平锁
// 设置ReentrantLock为公平锁
ReentrantLock lock = new ReentrantLock(true);

4、ReentrantLock方法

在idea编辑器里查看ReentrantLock的方法:

挑一些常用方法进行描述

  • lock():如果共享资源最初是空闲的,调用lock会进行计数加1,并将锁提供给线程。会进行累加
  • unlock():调用unlock方法,会进行计数减1,减为0时,释放资源锁
  • tryLock():为了避免锁竞争,可以使用tryLock,如果资源没有被其他线程持有,会返回true,加锁成功,已经被其他线程持有了,返回false,加锁失败
  • tryLock(long timeout, TimeUnit unit):这个尝试加锁方法,多了一个超时时间
  • lockInterruptably():如果资源空闲,则此方法获取锁,允许该线程在获取资源时被其他线程中断。如果该线程在获取锁过程,其它线程抢进来,获取锁过程会被中断并立即返回而不获取锁
  • getHoldCount():此方法返回资源上持有的锁数的计数。
  • isHeldByCurrentThread():如果当前线程持有资源锁,则此方法返回true

5、ReentrantLock例子

例子:进行大数据的count统计,开启20个线程,如何保证线程安全,计数的正确性?

  • volatile保证变量可见性
  • CountDownLatch协同多个线程
  • ReentrantLock 保证线程安全
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockCountExample {

    private static volatile int count = 0;
    static ReentrantLock lock = new ReentrantLock();

    public static void countHandler() {
        lock.lock();
        try {
            count++;
        }finally {
            lock.unlock();
        }
    }

    public static void doCountConcurrent() throws InterruptedException {
        int threads = 20;
        CountDownLatch cdl = new CountDownLatch(threads);
        for (int i = 0; i < threads; i++) {
            new Thread(() -> {
                for (int n = 0; n < 10000; n++) {
                    countHandler();
                }
                cdl.countDown();
            }).start();
        }
        try {
            cdl.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();
        doCountConcurrent();
        System.out.println("统计耗时:" + (System.currentTimeMillis() - start) + "ms");
        System.out.println("result:"+ ReentrantLockCountExample.count);
    }

}

相关文章