日常记录——多线程与高并发—ReentrantLock概念、原理、使用、ReentrantLock和synchronized比较

一、概念

ReentrantLock是接口lock的实现类,实现为基于CAS+AQS(AbstractQueuedSynchronizer构建锁和同步容器的框架)的可重入锁,默认为非公平锁,可通过构造器定义为公平锁。

二、原理

ReentrantLock获取锁的原理:先通过CAS判断锁资源是否被占用。如果此时已经有线程占据了锁,那就加入AQS队列并且被挂起。当锁被释放之后,排在队列队首的线程会被唤醒,然后CAS再次尝试获取锁。在这个时候,如果:
非公平锁:当排在队列队首获取锁资源,如果同时还有另一个线程进来尝试获取,那么有可能会让这个线程抢先获取,因为这时锁是未被占用的;
公平锁:如果同时还有另一个线程进来尝试获取,当它发现自己不是在队首的话,就会排到队尾,由队首的线程获取到锁。

三、使用

1.可重入:同一线程,获取两次锁,释放两次锁。

public class reentrantlock {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        new Thread(() -> {
            try {
                lock.lock();
                lock.lock();
            }catch (Exception e){

            }finally {
                lock.unlock();
                lock.unlock();
            }

        }).start();
    }
}

2.tryLock:尝试获取锁。获取失败返回fasle,执行逻辑。

public class trylocks {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        new Thread(() -> {
            boolean flag = false;
            Thread.currentThread().setName("线程A");
            try {
                System.out.println(Thread.currentThread().getName()+"开始执行");
                flag =  lock.tryLock(3, TimeUnit.SECONDS);
                if(flag){
                    Thread.currentThread().sleep(5000);
                    System.out.println(Thread.currentThread().getName()+"获取到锁");
                }else{
                    System.out.println(Thread.currentThread().getName()+"未获取到锁");
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                if(flag){
                    lock.unlock();
                }
            }

        }).start();

        new Thread(() -> {
            boolean flag = false;
            Thread.currentThread().setName("线程B");
            try {
                System.out.println(Thread.currentThread().getName()+"开始执行");
                lock.tryLock();
                flag =  lock.tryLock(3, TimeUnit.SECONDS);
                if(flag){
                    Thread.currentThread().sleep(5000);
                    System.out.println(Thread.currentThread().getName()+"获取到锁");
                }else{
                    System.out.println(Thread.currentThread().getName()+"未获取到锁");
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                if(flag) {
                    lock.unlock();
                }
            }

        }).start();

    }
}

3.公平锁:在锁上等待时间最长的线程将获得锁的使用权。大部分情况下我们使用非公平锁,因为其性能比公平锁好很多,不需要cpu切换线程。但是公平锁能够避免线程饥饿。

public class sameLock {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock(true);
        for(int i =0;i<5;i++){
            new Thread(() ->{
                Thread.currentThread().setName("线程"+Math.random());
                lock.lock();
                System.out.println(Thread.currentThread().getName()+"获取到锁");
                lock.unlock();
                lock.lock();
                System.out.println(Thread.currentThread().getName()+"获取到锁");
                lock.unlock();
            }).start();
        }
    }
}

4.lockInterruptibly:用来接收线程被打断信号,接收到抛出异常,防止线程因为获取不到锁资源一直在阻塞状态。

public class Interruptibly {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        Thread t1 = new Thread(() -> {
            System.out.println("t1开始执行");
            try {
                lock.lock();
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
            System.out.println("t1执行结束");
        });
        Thread t2 = new Thread(() -> {
            System.out.println("t2开始执行");
            try {
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                System.out.println("t2被打断");
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
            System.out.println("t2执行结束");
        });
        long start = System.currentTimeMillis();
        t1.start();
        t2.start();
        long end = System.currentTimeMillis();
        //等待时间超过5s,打断t2线程。
        while (end-start<5000){
            end = System.currentTimeMillis();
        }
        t2.interrupt();

    }
}

四、ReentrantLock和synchronized比较

1.synchronized是java内置的关键字,ReentrantLock是接口lock实现类。都可重入。
2.ReentrantLock可以尝试获取锁,根据结果执行逻辑,synchronized直接等待。
3.ReentrantLock需要手动释放锁,synchronized由JVM管理。
4.ReentrantLock可设定为公平锁。

猜你喜欢

转载自blog.csdn.net/weixin_43001336/article/details/107073457