详解Java并发包基石AQS

我会根据以下结构给出一份详细讲解“详解Java并发包基石AQS”的完整攻略。

什么是AQS

AQS是AbstractQueuedSynchronizer的缩写,翻译成中文可以叫做“抽象队列同步器”。它是java.util.concurrent包中的核心组成部分,也是各种同步组件(如ReentrantLock、Semaphore、CountDownLatch等)的基础。它提供了一种实现同步的可扩展的框架,并且大多数情况下不需要使用者直接使用AQS的核心方法,只需要继承它,然后实现一些简单的钩子方法,即可得到基本的同步功能。

AQS原理

AQS的核心回调方法是tryAcquire(int)tryRelease(int),它们分别会在AQS需要获取同步状态和释放同步状态的时候被调用。这两个方法的实现非常简单,只需要加几行CAS操作即可,如下所示:

protected boolean tryAcquire(int arg) {
   if (compareAndSetState(0, arg)) {
      setExclusiveOwnerThread(Thread.currentThread());
      return true;
   }
   return false;
}

protected boolean tryRelease(int arg) {
   if (getState() == 0) throw new IllegalMonitorStateException();
   setExclusiveOwnerThread(null);
   setState(0);
   return true;
}

其中getState()setState(int)方法是操作同步状态的方法,compareAndSetState(int, int)利用了Java中的CAS原子操作,尝试将同步状态从0改为传入的arg。如果同步状态为0,那么当前线程会获取同步状态并且成为同步状态的拥有者;如果同步状态不为0,那么当前线程尝试获取同步状态失败,并且需要加入同步队列中排队等待。

同步队列中的节点是继承自AbstractQueuedSynchronizer.Node的类,它包含了一个元素,主要用于表示在等待队列中处于节点的锁状态。包含如下字段:

volatile int waitStatus;
volatile Node prev;
volatile Node next;
volatile Thread thread;
Node nextWaiter;

其中,waitStatus表示节点的状态,prev和next表示前驱节点和后继节点,thread表示当前节点代表的线程,nextWaiter表示都是在等待队列中节点的链表结构。等待队列是一个FIFO的队列,被锁住的线程可能被插入到队列的尾部,等待其他线程释放锁。

AQS使用示例

下面代码是一个简单的使用AQS实现的锁的例子。它首先通过继承AbstractQueuedSynchronizer类并实现tryAcquire()tryRelease()方法,得到一个自定义的同步组件,然后通过这个同步组件来实现一个基于排它锁的缓存:

public class CustomLock extends AbstractQueuedSynchronizer {
    public CustomLock() {
        super();
    }

    @Override
    protected boolean tryAcquire(int arg) {
        if (compareAndSetState(0, arg)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }

    @Override
    protected boolean tryRelease(int arg) {
        if (getState() == 0) throw new IllegalMonitorStateException();
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }

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

    public void unlock() {
        release(1);
    }

    private final HashMap<String, Object> cache = new HashMap<>();

    public Object getValue(String key) {
        lock();
        try {
            return cache.get(key);
        } finally {
            unlock();
        }
    }

    public void setValue(String key, Object value) {
        lock();
        try {
            cache.put(key, value);
        } finally {
            unlock();
        }
    }
}

在这个例子中,我们定义了一个CustomLock类,它继承自AbstractQueuedSynchronizer类,并实现了tryAcquire()tryRelease()方法,最后定义了一个基于该锁的缓存。在getValue()setValue()方法中,我们调用了lock()unlock()方法来获取和释放锁。这样就可以保证在修改缓存的时候只有一个线程能够访问它。

下面给出另外一个简单的使用AQS的例子:

public class Semaphore {
    private final Sync sync;

    abstract static class Sync extends CustomLock {
        abstract void reducePermits(int reduction);
        abstract int tryAcquireShared(int acquires);
        abstract boolean tryReleaseShared(int releases);

        Sync(int permits) {
            setState(permits);
        }
    }

    private static final class NonfairSync extends Sync {
        NonfairSync(int permits) {
            super(permits);
        }

        @Override
        int tryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 || compareAndSetState(available, remaining)) {
                    return remaining;
                }
            }
        }

        @Override
        boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();
                int next = current + releases;
                if (next < current)
                    throw new Error("Maximum permit count exceeded");
                if (compareAndSetState(current, next))
                    return true;
            }
        }

        @Override
        void reducePermits(int reduction) {
            for (;;) {
                int current = getState();
                int next = current - reduction;
                if (next > current)
                    throw new IllegalArgumentException("Permit count underflow");
                if (compareAndSetState(current, next))
                    return;
            }
        }
    }

    public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }

    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

    public void release() {
        sync.releaseShared(1);
    }
}

在这个例子中,我们定义了一个简单的信号量(Semaphore),它包含了一个内部类Sync,它继承自上面的CustomLock类,同时包含了三个抽象方法reducePermits(int)tryAcquireShared(int)tryReleaseShared(int)。在NonfairSync类中,我们实现了这三个抽象方法,来实现信号量的功能。在Semaphore类的acquire()release()方法中,我们调用了acquireSharedInterruptibly(int)releaseShared(int)方法,来获取和释放信号量。

这些示例说明了如何通过继承AQS类,并实现一些简单的钩子方法,来实现基本的同步和语义。这些同步组件包括锁、读/写锁、信号量、倒计时锁等,都是用AQS框架来实现的。在使用这些组件的时候,我们一般不需要直接和AQS的方法打交道,只需要调用这些同步组件的公开API即可。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解Java并发包基石AQS - Python技术站

(0)
上一篇 2023年5月16日
下一篇 2023年5月16日

相关文章

  • python实现多线程的方式及多条命令并发执行

    Python 是一门支持多线程操作的语言,多线程操作可以提高代码的执行效率,而且在处理多任务的情况下也比较常用。下面是 Python 实现多线程的方式及多条命令并发执行的完整攻略。 Python 实现多线程的方式 Python 实现多线程通常有以下三种方式: 1. 使用 _thread 模块实现多线程 使用 _thread 模块实现多线程需要注意的事项: 线…

    多线程 2023年5月16日
    00
  • Java并发编程之threadLocal

    Java并发编程之threadLocal完整攻略 ThreadLocal是Java提供的一种线程封闭机制,可以实现线程间数据隔离。在并发编程中,线程间数据共享往往是很麻烦的问题,而ThreadLocal则可以帮助我们方便地解决这一问题。 ThreadLocal基本概念 以简单的方式来描述ThreadLocal,就是一个类似于Map的存储结构。不同之处在于,M…

    多线程 2023年5月16日
    00
  • 一个可交互的并发ping检测脚本

    针对“一个可交互的并发ping检测脚本”的完整攻略,我会从以下几个方面进行详细讲解。 1. 相关技术准备 在开始编写脚本之前,需要了解一些相关技术和工具,如: Python编程语言 并发编程 ping命令(Windows和Linux系统都支持) ping命令的Python封装库 2. 脚本设计与实现 2.1 设计思路 可以采用多线程的方式实现并发的ping检…

    多线程 2023年5月16日
    00
  • 详解Python并发编程之创建多线程的几种方法

    让我详细讲解一下“详解Python并发编程之创建多线程的几种方法”的完整攻略。 1. 为什么要使用多线程 在Python中使用多线程可以让一台计算机同时执行多个任务,从而提高程序的运行效率。具体来说,多线程可以在以下情况下使用: 需要处理大量IO密集型任务,如网络编程、读写文件等操作。 需要执行CPU密集型任务,如计算、图形渲染等操作。 需要同时处理多个任务…

    多线程 2023年5月16日
    00
  • JAVA多线程线程安全性基础

    关于JAVA多线程线程安全性,我给您讲一下我的理解。 什么是线程安全性? 在多线程开发中,往往有多个线程同时访问同一个共享资源,这时候就需要考虑线程安全性问题。当多个线程同时访问某一个对象时,如果不加以协调导致操作结果被破坏,则称为线程不安全。而当多个线程访问某一个对象时,不管运行时环境采用何种调度方式或者这些计算机内核以什么顺序来执行线程,而且在主调代码中…

    多线程 2023年5月17日
    00
  • 对python 多线程中的守护线程与join的用法详解

    对于“对python多线程中的守护线程与join的用法详解”的攻略,我会在以下几个方面进行详细说明: 线程和守护线程的概念 join方法的用法和作用 守护线程的用法和作用 示例说明 1. 线程和守护线程的概念 线程是指在进程中的执行序列,每个线程都有自己的栈、局部变量等,它们共享全局变量和静态变量等。线程是轻量级的进程,一个进程可以同时执行多个线程,各个线程…

    多线程 2023年5月16日
    00
  • java并发编程工具类JUC之LinkedBlockingQueue链表队列

    Java并发编程工具类JUC中,LinkedBlockingQueue是一种基于链表的阻塞队列。它可以支持多线程并发访问,是用于多线程交换数据的缓冲区。下面详细讲解一下该队列的使用方法。 LinkedBlockingQueue的特点和操作方法 特点 LinkedBlockingQueue内部采用了一种“等待-通知”机制,当试图向队列中添加元素时,如果队列已满…

    多线程 2023年5月17日
    00
  • SpringBoot项目的多文件兼多线程上传下载

    下面我将详细讲解SpringBoot项目的多文件兼多线程上传下载的完整攻略。 1. 多文件上传 1.1 前端页面实现 第一步是实现前端页面,让用户可以选择并上传多个文件。在html文件中,使用<input type=”file” multiple>标签实现多个文件上传,代码如下: <form action="/upload&quo…

    多线程 2023年5月16日
    00
合作推广
合作推广
分享本页
返回顶部