线程基础知识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并发编程艺术》)

原文地址:https://www.cnblogs.com/perferect/p/13626837.html