Java并发编程艺术之Java中的锁

版权声明:本博客为技术原创, 未经允许,谢绝转载 !!! https://blog.csdn.net/yewandemty/article/details/82287372

Java并发编程艺术之Java中的锁

    本文章主要介绍Java并发包中与锁相关的API和组件,会从1)使用 、2)实现 两个方面进行介绍 ,下面是主要包含的内容:

  • Lock接口
  • 队列同步器(AQS)
  • 重入锁
  • 读写锁
  • LockSupport工具
  • Condition接口

一、Lock接口

    锁的简单介绍: 锁可以控制多个线程访问共享资源的方式,可以防止多个线程同时访问共享资源,
    锁的实现方式: 在不同JDK版本,实现锁的方式不同

  • JDK5 之前: 通过synchronized 关键字实现锁的功能
  • JDK5 之后: 增加并发包(java.util.concurent)中Lock来实现锁功能(synchronized依然可以实现锁的功能)

    synchronized和Lock差异性

  • synchronized可以隐式的获取和释放锁(简化了同步管理、扩展性比较差), 而Lock需要显示的获取和释放锁
  • synchronized不具有中断获取锁、超时获取锁的功能等同步特性,Lock具有锁释放/获取的可操作性, 具有可中断获取锁,具有超时获取锁等同步特性

下面是Lock提供的synchronized关键字不具备的特性:

特性 描述
尝试非阻塞的获取锁 当前线程尝试获取锁,如果这一时刻锁没有被其它线程获取到,则成功获取并持有锁
能被中断的获取锁 与synchronized不同,获取到锁的线程可以响应中断,当获取到锁的线程响应中断时,会抛出中断异常,同时释放锁
超时获取锁 在指定的截至时间之前获取锁,如果截止时间之前任然没有获取到锁,则返回

    Lock API
Lock API

图1、Lock API

二、队列同步器(AQS)

    队列同步器(AbstractQueuedSynchronizer), 是用来构建或者其它同步组件的基础框架; 同步器的使用方式是继承,在抽象方法的实现过程中会通过getState()、setState(int newState)、compareAndSetState(int expect, int updateState)方法对同步状态进行修改 。

注:继承同步器(AQS)的子类推荐为同步组件的静态内部类,同步器自身没有实现任何同步接口,仅仅是定义了同步状态获取和释放的若干方法,以便供自定义同步组件使用,同步器即支持独占式的获取同步状态,也支持共享式的获取状态, 比如: ReentrantLock、ReentrantReadWriteLock、CountDownLatch。

    同步器(AQS)、自定义组件、自定义锁三者之间的关系 如下图:
同步器(AQS)、同步组件、自定义锁之间的关系

图2、同步器(AQS)、同步组件、自定义锁之间的关系

代码实现示例如下:

public class ReentrantLock implements Lock, java.io.Serializable { // 自定义锁, 此示例代码是重入锁   
    private final Sync sync;

    abstract static class Sync extends AbstractQueuedSynchronizer { //同步组件, 其中Sync的父类 AbstractQueuedSynchronizer 是队列同步器
        private static final long serialVersionUID = -5179523762034025860L;

        abstract void lock();

        final boolean nonfairTryAcquire(int acquires) {
            // ...nonfairTryAcquire 的实现逻辑
        }

        protected final boolean tryRelease(int releases) {
            // ... tryRelease 的实现逻辑
        }

        protected final boolean isHeldExclusively() {
            // ... isHeldExclusively 的实现逻辑
        }

        final ConditionObject newCondition() {
            // ... newCondition 的实现逻辑
        }
    }
}
  • 锁是面向实现者的,它定义了使用者和锁交互的接口,隐藏了实现的细节
  • 同步器是面向锁的实现者的,它简化了锁的实现方式,屏蔽了同步状态管理、线程排队、等待/唤醒 等底层操作 。

1. 队列同步器的接口与示例
    同步器是通过模板方法来设计的,因此使用者需要继承同步器,并重写指定的方法,随后将同步器组合在同步组件实现中,最后调用使用者重写的模板方法

下面是同步器可重写的方法和同步器提供的模板方法
同步器可重写的方法

图3、同步器可重写的方法

同步器提供的模板方法

图4、同步器提供的模板方法

针对图4同步器提供的模板方法可以分为3类

  • 独占式获取和释放同步状态
  • 共享式获取和释放同步状态
  • 查询同步队列中等待线程的状况

这里以独占锁的示例初步了解同步器的工作原理
独占锁: 同一个时刻只有一个线程获取到锁, 而其它获取锁的线程只能处于同步队列中等待。
下面是代码示例:

public class MutexThread {
    //2. 将需要的操作代理到Sync上
    private final Sync sync = new Sync();

    public void lock(){
        sync.acquire(1);
    }

    public boolean tryLock() {
        return sync.tryAcquire(1);
    }

    public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

    public boolean unlock() {
        return sync.release(1);
    }

    public Condition newCondition() {
        return sync.newCondition();
    }

    public boolean isLocked(){
        return sync.isHeldExclusively();
    }

    public boolean hasQueuedThreads() {
        return sync.hasQueuedThreads() ;
    }

    public void lockInterrupterly() throws InterruptedException{
        sync.acquireInterruptibly(1);
    }

    //1. 定义自定义组件, 静态内部类
    private static class Sync extends AbstractQueuedSynchronizer {
        // 同步器是否被当前线程独占
        @Override
        protected boolean isHeldExclusively() {
            return getState() == 1 ;
        }
        //当state状态为0的时候获取锁
        @Override
        public boolean tryAcquire(int acquries) {
            if(compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false ;
        }
        //释放锁, 将状态设置为0
        @Override
        protected boolean tryRelease(int release) {
            if(getState() == 0) {
                throw new IllegalMonitorStateException();
            }
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

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

针对上面示例代码做简单介绍:

  • MutexThread是一个自定义独占锁, 同一时刻只允许一个线程占有锁
  • Sync是静态内部类, 它继承了队列同步器(AbstractQueuedSynchronizer), 该内部类实现了独占式获取和释放同步状态
  • tryAcquire(int acquires)方法中,如果经过CAS设置成功,会将同步状态设置为1 , 表示获取同步状态成功了
  • tryRelease(int releases)方法中,只是将同步状态设置为0,表示释放同步状态成功,如果之前已经释放(getState() == 0)会抛出非法监视状态异常(IllegalMonitorStateException)
  • 使用者并不会直接和内部的同步器实现交互,而是通过MutexThread提供的方法,

2. 队列同步器的实现分析
    本小节包含的内容如下:

  • 同步队列
  • 独占式同步状态获取与释放
  • 共享式同步状态获取与释放
  • 超时获取同步状态

1) 同步队列
    同步队列是一个FIFO的双向队列,
1. 当线程获取同步状态资源失败时, 同步器(AQS)会将当前线程构造成一个Node, 并加入同步队列中,
2. 处于同步队列中的线程处于阻塞状态, 在同步状态释放时,会唤醒首节点中的线程,使其再次尝试获取同步状态

    阻塞队列中添加的节点(Node)信息定义如下:

static final class Node {

        static final Node SHARED = new Node();

        static final Node EXCLUSIVE = null;

        static final int PROPAGATE = -3;

        volatile int waitStatus;

        volatile Node prev;

        volatile Node next;

        volatile Thread thread;

        Node nextWaiter;

        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {    // Used to establish initial head or SHARED marker
        }

        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

从节点Node定义包含如下信息:

  • 获取同步状态失败线程的引用(volatile Thread thread ;)
  • 等待状态(volatile int waitStatus ;)
  • 当前线程的前驱节点(volatile Node prev) 和后继节点(volatile Node next)

节点的属性信息及描述

图5、节点的属性信息及描述

    关于同步队列添加节点\释放节点的操作,可以通过下面的图示进行了解:
同步队列添加节点、释放节点

图6、同步队列添加节点、释放节点

猜你喜欢

转载自blog.csdn.net/yewandemty/article/details/82287372