7-ReentrantLock底层原理分析

一,Lock

Lock 在 J.U.C 中是最核心的组件。

1,Lock实现

Lock 本质上是一个接口,它定义了释放锁和获得锁的抽象方法,定义成接口就意味着它定义了锁的一个标准规范,也同时意味着锁的不同实现。实现 Lock 接口的类有很多,以下为几个常见的锁实现。

ReentrantLock:表示重入锁,它是唯一一个实现了 Lock 接口的类。重入锁指的是线程在获得锁之后,再次获取该锁不需要阻塞,而是直接关联一次计数器增加重入次数。

ReentrantReadWriteLock:重入读写锁,它实现了 ReadWriteLock 接口,在这个类中维护了两个锁,一个是 ReadLock,一个是 WriteLock,他们都分别实现了 Lock接口。读写锁是一种适合读多写少的场景下解决线程安全问题的工具,基本原则是: 读和读不互斥、读和写互斥、写和写互斥。也就是说涉及到影响数据变化的操作都会存在互斥。

StampedLock: stampedLock 是 JDK8 引入的新的锁机制,可以简单认为是读写锁的一个改进版本,读写锁虽然通过分离读和写的功能使得读和读之间可以完全并发,但是读和写是有冲突的,如果大量的读线程存在,可能会引起写线程的饥饿。stampedLock 是一种乐观的读策略,使得乐观锁完全不会阻塞写线程。

2,Lock 的类关系图

Lock 有很多的锁的实现,但是直观的实现是 ReentrantLock 重入锁

在这里插入图片描述

void lock() // 如果锁可用就获得锁,如果锁不可用就阻塞直到锁释放
void lockInterruptibly() // 和lock()方法相似, 但阻塞的线程 可 中 断 , 抛 出java.lang.InterruptedException 异常
boolean tryLock() // 非阻塞获取锁;尝试获取锁,如果成功返回 true
boolean  tryLock(long timeout, TimeUnit timeUnit) //带有超时时间的获取锁方法
void unlock() // 释放锁

二,ReentrantLock 重入锁

重入锁,表示支持重新进入的锁,也就是说,如果当前线程 t1 通过调用 lock 方法获取了锁之后,再次调用 lock,是不会再阻塞去获取锁的,直接增加重试次数就行了。synchronized 和 ReentrantLock 都是可重入锁。

1,重入锁的设计目的

比如调用 demo 方法获得了当前的对象锁,然后在这个方法中再去调用demo2,demo2 中的存在同一个实例锁,这个时候当前线程会因为无法获得demo2 的对象锁而阻塞,就会产生死锁。重入锁的设计目的是避免线程的死锁。

public class ReentrantDemo {
   
    public synchronized void demo() {
   
        System.out.println("begin:demo");
        demo2();
    }

    public void demo2() {
   
        System.out.println("begin:demo1");
        synchronized (this) {
   
        }
    }

    public static void main(String[] args) {
   
        ReentrantDemo rd = new ReentrantDemo();
        new Thread(rd::demo).start();
    }
}

2,ReentrantLock 使用案例

public class AtomicDemo {
   
    private static int count = 0;
    static Lock lock = new ReentrantLock();

    public static void inc() {
   
        lock.lock();
        try {
   
            Thread.sleep(1);
            count++;
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }finally {
   
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
   
        for (int i = 0; i < 1000; i++) {
   
            new Thread(() -> AtomicDemo.inc()).start();
        }
        Thread.sleep(3000);
        System.out.println("result:" + count);
    }
}

3,ReentrantReadWriteLock

以前理解的锁,基本都是排他锁,也就是这些锁在同一时刻只允许一个线程进行访问,而读写所在同一时刻可以允许多个线程访问,但是在写线程访问时,所有的读线程和其他写线程都会被阻塞。读写锁维护了一对锁,一个读锁、一个写锁;一般情况下,读写锁的性能都会比排它锁好,因为大多数场景读是多于写的。在读多于写的情况下,读写锁能够提供比排它锁更好的并发性和吞吐量.

public class LockDemo {
   

    static Map<String, Object> cacheMap = new HashMap<>();
    static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    static Lock read = rwl.readLock();
    static Lock write = rwl.writeLock();

    public static final Object get(String key) {
   
        System.out.println(" 开始读取数据");
        read.lock(); // 读锁
        try {
   
            return cacheMap.get(key);
        } finally {
   
            read.unlock();
        }
    }

    public static final Object put(String key, Object value) {
   

        System.out.println(" 开始写数据");
        write.lock();
        try {
   
            return cacheMap.put(key, value);
        } finally {
   
            write.unlock();
        }
    }
}

在这个案例中,通过 hashmap 来模拟了一个内存缓存,然后使用读写锁来保证这个内存缓存的线程安全性。当执行读操作的时候,需要获取读锁,在并发访问的时候,读锁不会被阻塞,因

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值