IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 【JUC系列】LOCK框架系列之六 核心锁类之ReentrantLock -> 正文阅读

[Java知识库]【JUC系列】LOCK框架系列之六 核心锁类之ReentrantLock

ReentrantLock


可重入指任意线程在获取到锁之后可以再次获取到该锁而不会被该锁阻塞。

可重入锁指支持可以重入的锁,该锁可以支持一个线程对资源的重复加锁。

线程再次获取锁问题: 锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是,则可以再次获取成功。

锁的最终释放问题:线程重复n此获取了锁,随后在第n次释放该锁后,其他线程能够获取到该锁。锁的最终释放要求对于获取进行计数自增,计数表示当前锁被线程重复获取的次数,而锁被释放时,计数自减,当计数等于0时表示该锁成功释放。

主要成员

ReentrantLock实现了Lock接口,并在内部创建了一个内部类Sync继承了AQS类。在Sync上又延伸出公平锁和非公平锁。在绝对时间上,先对锁进行获取请求一定先获取到,则该锁是公平的,反之,锁是非公平锁。
在这里插入图片描述

构造函数

ReentrantLock的构造函数如下

    // 无参构造函数 默认是非公平锁
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    // 有参构造函数 true是公平锁 false是非公平锁
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

Sync类

sync继承了AbstractQueuedSynchronizer。主要还是依托于AQS进行锁的管理。初识AQS

存在如下方法和作用如下:

方法名说明
void lock();获取锁,由子类实现
boolean nonfairTryAcquire(int acquires)非公平方式获取同步状态 true成功获取;false获取失败
boolean tryRelease(int releases)释放状态,true成功释放;false释放失败
boolean isHeldExclusively()判断资源是否被当前线程占有 true代表当前线程占用,false代表其他线程占用
ConditionObject newCondition()创建一个新的condition对象
Thread getOwner()或者当前占用线程,null代表为有占用者线程
int getHoldCount()返回目前占用的状态(次数) 0代表未被占用
boolean isLocked()资源是否被锁了(占用),true已被占用,false未被占用
void readObject(java.io.ObjectInputStream s)自定义反序列化逻辑

源码部分

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * Performs {@link Lock#lock}. The main reason for subclassing
         * is to allow fast path for nonfair version.
         * 获取锁的方法,由子类具体实现
         */
        abstract void lock();

        /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         * 非公平方式获取锁
         */
        final boolean nonfairTryAcquire(int acquires) {
            // 当前线程
            final Thread current = Thread.currentThread();
            // 获取状态
            int c = getState();
            // 0代表目前没有线程竞争该锁
            if (c == 0) {
                // 通过CAS操作占用锁,占用成功的话,直接结束该方法
                if (compareAndSetState(0, acquires)) {
                    // 将当前线程变成资源的占用者
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 当前线程就是该锁的占用者
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                // 对线程再次获取该锁的时候,对状态值进行自增
                setState(nextc);
                return true;
            }
            return false;
        }

        //试图在共享模式下获取对象状态,此方法应该查询是否允许它在共享模式下获取对象状态,如果允许,则获取它
        // 释放同步状态 true表示锁成功释放
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            // 当同步状态为0,说明资源以及可以释放了,清空资源占用者
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

        // 判断资源是否被当前线程占有
        protected final boolean isHeldExclusively() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        // Methods relayed from outer class

        // 返回资源的占用线程
        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

        // 返回目前占用的状态
        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }

        // 资源是否被锁了(占用)
        final boolean isLocked() {
            return getState() != 0;
        }

        /**
         * Reconstitutes the instance from a stream (that is, deserializes it).
         自定义反序列化逻辑
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

NonfairSync 非公平锁

继承了Sync,提供了非公平方式的获取方法lock()

   static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         * 获取锁
         */
        final void lock() {
            // 直接尝试CAS占资源,如果成功就将自己变为占用者
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                // 锁已经被占用,或者set失败  以独占模式获取对象,忽略中断估计排队去了
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

FairSync公平锁

同样继承了Sync,提供了公平方式的获取方法lock()tryAcquire(int acquires)

    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        // 以独占模式获取对象,忽略中断估计排队去了
        final void lock() {
            acquire(1);
        }

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         * 尝试公平获取锁
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            // 0代表无占用者
            if (c == 0) {
                // 如果当前线程位于队列的头部或队列为空,则CAS操作获取锁
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    // 成功获取锁,设置占用者
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 当前线程就是该锁的占用者
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                // 对线程再次获取该锁的时候,对状态值进行自增
                setState(nextc);
                return true;
            }
            return false;
        }
    }

示例分析

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class FairAndUnfairTest {
    private static Lock fairLock = new ReentrantLock2(true);
    private static Lock unfairLock = new ReentrantLock2(false);


    public static void main(String[] args) throws InterruptedException {
        System.out.println("非公平锁");
        unfair();
        System.out.println("公平锁");
        fair();
    }

    public static void unfair() throws InterruptedException {
        testLock(unfairLock);
    }


    public static void fair() throws InterruptedException {
        testLock(fairLock);
    }

    private static void testLock(Lock lock) throws InterruptedException {

        for (int i = 0; i < 3; i++) {
            Thread thread = new Thread(new Job(lock)) {
                @Override
                public String toString() {
                    return getName();
                }
            };
            thread.setName("t_" + i);
            thread.start();
        }
        Thread.sleep(11000);
    }

    private static class Job extends Thread {

        private Lock lock;

        public Job(Lock lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            for (int i = 0; i < 2; i++) {
                //  去竞争锁
                lock.lock();
                try {
                    s();
                    Thread.sleep(1000);
                    System.out.println("获取锁的当前线程[" + Thread.currentThread().getName() + "],第" + (i + 1) + "次执行, 同步队列中的线程" + ((ReentrantLock2) lock).getQueueThreads() + "");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // 当前线程释放锁后,唤醒同步队列中的首结点去获取锁(公平式),其他线程会与首结点对应的线程竞争锁(非公平)
                    lock.unlock();
                }
            }
        }

        private void s(){
            lock.lock();
            try {
                System.out.println("获取锁的当前线程[" + Thread.currentThread().getName() + "],重入执行, 同步队列中的线程" + ((ReentrantLock2) lock).getQueueThreads() + "");
            } finally {
                // 当前线程释放锁后,唤醒同步队列中的首结点去获取锁(公平式),其他线程会与首结点对应的线程竞争锁(非公平)
                lock.unlock();
            }
        }

    }

    private static class ReentrantLock2 extends ReentrantLock {
        public ReentrantLock2(boolean fair) {
            super(fair);
        }

        public Collection<Thread> getQueueThreads() {
            List<Thread> threadList = new ArrayList<>(super.getQueuedThreads());
            Collections.reverse(threadList);
            return threadList;
        }
    }
}

执行结果

非公平锁
获取锁的当前线程[t_0],重入执行, 同步队列中的线程[t_1, t_2]
获取锁的当前线程[t_0],第1次执行, 同步队列中的线程[t_1, t_2]
获取锁的当前线程[t_0],重入执行, 同步队列中的线程[t_1, t_2]
获取锁的当前线程[t_0],第2次执行, 同步队列中的线程[t_1, t_2]
获取锁的当前线程[t_1],重入执行, 同步队列中的线程[t_2]
获取锁的当前线程[t_1],第1次执行, 同步队列中的线程[t_2]
获取锁的当前线程[t_1],重入执行, 同步队列中的线程[t_2]
获取锁的当前线程[t_1],第2次执行, 同步队列中的线程[t_2]
获取锁的当前线程[t_2],重入执行, 同步队列中的线程[]
获取锁的当前线程[t_2],第1次执行, 同步队列中的线程[]
获取锁的当前线程[t_2],重入执行, 同步队列中的线程[]
获取锁的当前线程[t_2],第2次执行, 同步队列中的线程[]
公平锁
获取锁的当前线程[t_0],重入执行, 同步队列中的线程[]
获取锁的当前线程[t_0],第1次执行, 同步队列中的线程[t_1, t_2]
获取锁的当前线程[t_1],重入执行, 同步队列中的线程[t_2, t_0]
获取锁的当前线程[t_1],第1次执行, 同步队列中的线程[t_2, t_0]
获取锁的当前线程[t_2],重入执行, 同步队列中的线程[t_0]
获取锁的当前线程[t_2],第1次执行, 同步队列中的线程[t_0, t_1]
获取锁的当前线程[t_0],重入执行, 同步队列中的线程[t_1, t_2]
获取锁的当前线程[t_0],第2次执行, 同步队列中的线程[t_1, t_2]
获取锁的当前线程[t_1],重入执行, 同步队列中的线程[t_2]
获取锁的当前线程[t_1],第2次执行, 同步队列中的线程[t_2]
获取锁的当前线程[t_2],重入执行, 同步队列中的线程[]
获取锁的当前线程[t_2],第2次执行, 同步队列中的线程[]

  • 什么是可重入,什么是可重入锁? 它用来解决什么问题?
  • ReentrantLock的核心是AQS,那么它怎么来实现的,继承吗? 说说其类内部结构关系。
  • ReentrantLock是如何实现公平锁的?
  • ReentrantLock是如何实现非公平锁的?
  • ReentrantLock默认实现的是公平还是非公平锁?
  • 使用ReentrantLock实现公平和非公平锁的示例?
  • ReentrantLock和Synchronized的对比?
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-29 11:57:31  更:2022-04-29 11:57:34 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:12:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码