java锁-Lock

java锁-Lock

详细讨论java中Lock接口底层加锁原理和应用。



前言

Lock接口是java中重要的锁之一,ReentrantLock、ReentrantReadWriteLock.WriteLock,ReentrantReadWriteLock.ReadLock是几个重要的实现之一,其中ReentrantLock是我们常用的实现类。本文主要讲解ReentrantLock原理和使用,ReentrantLock基于AQS实现,所以在阅读本文前请先了解AQS原理,对AQS不是很了解请移步《java锁-AQS

Lock接口源码


public interface Lock {

    /**
     * 加锁,获取锁过程中如果线程中断,则继续获取不会中断,只在获取锁后才中断当前线程
     */
    void lock();

    /**
     * 加锁,获取锁过程中如果线程中断,直接抛InterruptedException异常,其他线程可以通过中断该线程来竞争资源
     */
    void lockInterruptibly() throws InterruptedException;

    /**
     * 当前线程尝试获取锁
     * true: 获取锁成功
     * false: 获取锁失败
     */
    boolean tryLock();

    /**
     * 当前线程尝试获取锁在一段时间内
     * true: 获取锁成功
     * false: 获取锁失败
     * @param time 最长等待时间
     * @param unit 时间单位
     */
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    /**
     * 释放锁
     */
    void unlock();

    /**
     * 返回一个条件对象
     */
    Condition newCondition();
}

ReentrantLock使用

    /**
     * 定义一个可重入锁
     */
  private final  Lock lock = new ReentrantLock();
  
  public boolean printInfo(){
      /**
       * 开始上锁
       */
      lock.lock();
      try{
          /**
           * 让线程休眠1000毫秒
           */
           Thread.sleep(1000);
          /**
           *  打印一条数据
           */
          System.out.println("this is ReentrantLock test information");
          System.out.println("当前执行线程=>" + Thread.currentThread().getName());

      } catch (Exception e) {
          e.printStackTrace();
      } finally {
          /**
           * 释放锁, lock.unlock() 必须放在finally的第一行代码,防止其他代码出现异常而不能及时释放锁
           */
        lock.unlock();
      }
      return true;
  }

至于lockInterruptibly()、tryLock()和tryLock(long time, TimeUnit unit)用法和lock()用法一致,读者自己去测试吧。

ReentrantLock实现原理

在ReentrantLook对象中维护一个sync变量,这个就是Sync对象,Sync对象继承了AbstractQueuedSynchronizer,到这里是不是有一点豁然开朗了,ReentrantLook中所有加锁操作都是基于sync实现的,下面看看源码:


public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    /** 定义了一个同步变量*/
    private final Sync sync;

    /**
     * 自定义一个同步器Sycn
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * 定义一个加锁抽象方法,有具体子类实现
         */
        abstract void lock();

        /**
         * 非公平获取锁,每一次都直接尝试获取锁,不管有没有其他等待队列
         */
        final boolean nonfairTryAcquire(int acquires) {
            // 获取当前线程
            final Thread current = Thread.currentThread();
            // 获取AQS中共享资源state的值
            int c = getState();
            // 若当前未有线程持有
            if (c == 0) {
                 // 直接通过CAS修改state值为acquires(一般acquires等于1)
                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");
                    // 修改state为叠加的新值,由于是同一个线程操作不存在线程安全问题
                setState(nextc);
                // 返回获取线程成功
                return true;
            }
              // 返回获取线程失败
            return false;
        }

       // 尝试释放锁,这是AQS的自定义实现
        protected final boolean tryRelease(int releases) {
            // 将AQS的state数量减releases(releases一般为1)
            int c = getState() - releases;
            // 如果当前线程不等独占线程,说明是其他线程操作,则抛出IllegalMonitorStateException异常
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            // 如果AQS的state-1 = 0,说明锁释放成功,如果是重入加锁多次,则释放次数必须和重入次数一致,方可解锁
            if (c == 0) {
                // 将解锁状态设置为成功
                free = true;
                // 设置独占线程为null
                setExclusiveOwnerThread(null);
            }
            // 修改AQS的值
            setState(c);
            return free;
        }
        // 判断当前线程是否是独占线程
        protected final boolean isHeldExclusively() {
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        // 生成一个新的ConditionObject对象
        final ConditionObject newCondition() {
            return new ConditionObject();
        }
    }

    /**
     * 非公平锁的实现
     */
    static final class NonfairSync extends Sync {

        /**
         * 从写加锁落差
         */
        final void lock() {
            // CAS修改AQS的state值,修改成功则说明state等于0
            if (compareAndSetState(0, 1))
                 // 获取锁成功,将独占线程设置为当前线程
                setExclusiveOwnerThread(Thread.currentThread());
            else
                // 获取不到锁,则调用AQS的acquire(1)执行获取锁逻辑
                acquire(1);
        }

        // AQS尝试获取锁的实现
        protected final boolean tryAcquire(int acquires) {
            // 调用父类Sync.nonfairTryAcquire(acquires)尝试获取锁方法
            return nonfairTryAcquire(acquires);
        }
    }

    /**
     * 公平锁实现方式
     */
    static final class FairSync extends Sync {
          // 直接调用AQS的acquire(1)执行获取锁逻辑
        final void lock() {
            acquire(1);
        }
        
         // AQS尝试获取锁的实现(这里是公平获取锁的自定义实现)
        protected final boolean tryAcquire(int acquires) {
            // 获取当前线程
            final Thread current = Thread.currentThread();
            // 获取AQS中共享资源state的值
            int c = getState();
             // c == 0说明当前未有线程持有
            if (c == 0) {
                /**
                 * hasQueuedPredecessors() 在当前节点之前是否有等待的队列(这是公平的体现,只有队列头才能获取锁,其他节点必须等待)
                 * compareAndSetState(0, acquires) CAS修改AQS的state值为acquires,修改成功则说明竞争锁成功
                 */
                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;
        }
    }
    
    // 默认采用非公平锁模式
    public ReentrantLock() {
        sync = new NonfairSync();
    }
    
    // 可以通过fair参数来选择使用公平锁还是非公平锁
    // fair = true 公平锁
    // fair = false 非公平锁
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
    // 加锁实现
    public void lock() {
        sync.lock();
    }

    // 释放锁实现
   public void unlock() {
        sync.release(1);
    }
}

到这里源码分析就结束了,得出以下几个结论:

1、ReentrantLock 是通过AQS实现加锁
2、ReentrantLock可使用公平锁和非公平锁两种模式,可通过构造函数传fair实现
3、fair = true 启用公平锁,fair = false启用非公平锁
4、为了提高性能默认ReentrantLock 是非公平锁(推荐)


如果对您有帮助,请支持博主点一个鼓励的赞
原创不易,转载请标明来源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罗德阿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值