• 线程基础知识05- ReentrantLock重入锁


         ReentrantLock也叫重入锁,可以对同一线程资源进行重复加锁。通过lock()方法可以显式的加锁,并且再次调用lock(),不会出现阻塞的情况

    Sync子类提供锁的基本实现机制

    • 非公平锁的获取

      • 获取独占锁后,增加状态码
     //加锁
     final void lock() {
                if (compareAndSetState(0, 1))//获取到锁,增加锁状态码为1
                    setExclusiveOwnerThread(Thread.currentThread());
                else
                    /**
                     * 调用AQS中的acquire方法,加入到同步队列中
                     * 循环调用tryAcquire方法,当前节点的前驱是头节点时,设置当前节点为头节点
                     */    
                    acquire(1);
            }
    
    
     final boolean nonfairTryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
                if (c == 0) {//表示当前状态为空,设置当前线程为独占锁线程
                    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;
            }
    
    • 释放锁操作

      • 释放锁后更新减少状态码,同步状态为0时,表示锁释放成功
    protected final boolean tryRelease(int releases) {
                int c = getState() - releases;
                if (Thread.currentThread() != getExclusiveOwnerThread())
                    throw new IllegalMonitorStateException();
                boolean free = false;
                if (c == 0) {//状态为0时,独占线程置为空,释放线程
                    free = true;
                    setExclusiveOwnerThread(null);
                }
                setState(c);//更新状态
                return free;
            }
    

    公平锁FairSync

    • 公平锁的获取锁方法

      • 同步队列的头节点进行获取锁,如果成功,则返回true,如果未获取成功,返回false
    protected final boolean tryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
                if (c == 0) {
                     /**
                      * 1.当前线程节点为头节点,并通过CAS成功将state设置成acuires值
                      * 返回true
                      */   
                    if (!hasQueuedPredecessors() &&
                        compareAndSetState(0, acquires)) {
                        setExclusiveOwnerThread(current);
                        return true;
                    }
                }
                 /**
                  * 当前线程和独占线程一致时
                  * 1.更改状态;
                  * 2.返回获取锁成功  
                  */    
                else if (current == getExclusiveOwnerThread()) {
                    int nextc = c + acquires;
                    if (nextc < 0)
                        throw new Error("Maximum lock count exceeded");
                    setState(nextc);
                    return true;
                }
                return false;
            }
    
     public final boolean hasQueuedPredecessors() {
            Node t = tail; // Read fields in reverse initialization order
            Node h = head;
            Node s;
            return h != t &&
                ((s = h.next) == null || s.thread != Thread.currentThread());
        }
    

    非公平锁NonfairSync

    • 直接调用SYN的nofairTryAcquire方法
     protected final boolean tryAcquire(int acquires) {
                return nonfairTryAcquire(acquires);
            }
    
    

    公平锁和非公平锁创建

    • 通过传入true或false,初始化创建公平锁或非公平锁

    • 默认是不公平锁

     public ReentrantLock(boolean fair) {
            sync = fair ? new FairSync() : new NonfairSync();
        }
    
    public ReentrantLock() {
            sync = new NonfairSync();
        }
    

    公平锁和非公平锁的优缺点

    • 公平锁:

      优点:保证了按照锁的获取的时间长短,时间最长的先获取到锁。会获取当前节点所在等待队列的位置,必须按照位置执行。

      缺点:

      • 因为要判断前驱,且严格按照同步队列顺序,因此效率更低

      • 也因为顺序执行,所以多次获取锁操作,会造成上下文切换冗余,切换次数更多

    • 非公平锁:

      优点: 因为只需要判断状态码,因此,同一个锁再次获取到的概率比较大,所以锁切换次数少,效率比较高。保证有更大的吞吐量。

      缺点: 同一个锁的多次调用,容易造成,单个锁调用频繁,线程出现“饥饿”状态;

    (图片摘自《Java并发编程艺术》)

  • 相关阅读:
    成都磨子桥技工学校 / 数据结构 Challenge 4
    圆桌问题(网络流24题)
    试题库问题(网络流24题)
    [AHOI2005]航线规划
    [AMPPZ2014]The Prices
    方格取数(网络流24题)
    太空飞行计划问题(网络流24题)
    Linux 学习3
    Linux 学习2
    Linux 学习1
  • 原文地址:https://www.cnblogs.com/perferect/p/13626837.html
Copyright © 2020-2023  润新知