详解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日

相关文章

  • C# 多线程中经常访问同一资源可能造成哪些问题

    C# 多线程中经常访问同一资源可能造成以下问题: 竞态条件 死锁 竞态条件 当多个线程在访问同一资源时,它们可能会相互干扰,以致结果无法确定或不正确。这种情况称为“竞态条件”,很难被预先检测,常见的情况包括: 多个线程尝试同时读取和修改同一个变量 多个线程尝试同时写入同一个文件 多个线程尝试同时访问同一个网络连接 例如,考虑一个账户余额查询和转账应用。我们在…

    多线程 2023年5月16日
    00
  • Java并发计数器的深入理解

    Java并发计数器的深入理解 什么是Java并发计数器 Java并发计数器是一项重要的多线程技术,它可以在多线程环境下高效地实现数据的计数。 Java并发计数器的本质是使用CAS原子操作实现的,CAS的全称是Compare and Swap,即“比较并交换”,CAS提供了一种无锁化的解决方案,让多线程同时更新同一个数据变得更加高效。 实现原理 在并发计数器的…

    多线程 2023年5月16日
    00
  • 详细分析Java并发集合ArrayBlockingQueue的用法

    下面是详细的攻略: Java并发集合ArrayBlockingQueue的用法分析 1. 简介 ArrayBlockingQueue是Java中的一个并发集合,是线程安全的,可以在生产者和消费者之间传递数据。它是一个有界队列,具有固定的大小,即在构造时指定队列的容量。 2. 常用方法 ArrayBlockingQueue有许多常用的方法,下面是其中的一些: …

    多线程 2023年5月16日
    00
  • Java多线程并发synchronized 关键字

    Java多线程并发synchronized 关键字攻略 什么是synchronized synchronized是Java中用于控制并发访问的关键字,它能够确保程序在执行synchronized代码块或方法时,同一时刻只有一个线程可以进入,其他线程必须等待,直到当前线程执行完毕。 如何使用synchronized 在Java中,synchronized可以用…

    多线程 2023年5月16日
    00
  • java多线程并发executorservice(任务调度)类

    Java多线程并发的的Executors类提供了一种创建和管理线程池的方式,其中Executors.newFixedThreadPool(int n)和Executors.newCachedThreadPool()方法最常用。 Executors.newFixedThreadPool ExecutorService executor = Executors.…

    多线程 2023年5月16日
    00
  • 总结java多线程之互斥与同步解决方案

    这里是关于“总结java多线程之互斥与同步解决方案”的完整攻略。 一、什么是互斥与同步 多线程编程中,访问共享资源可能会导致数据不安全或者结果不一致的情况,因此需要保证多个线程对共享资源的访问是互斥的,同时又能达到协同工作的目的。在 Java 多线程中,提供了两种机制来实现这个目的:互斥和同步。 互斥:指当多个线程同时访问共享资源时,只允许其中的一个线程在访…

    多线程 2023年5月16日
    00
  • Java多线程定时器Timer原理及实现

    Java多线程定时器Timer原理及实现 什么是定时器Timer? 定时器是一个可以定期执行特定任务的程序,可以让我们在特定时间或间隔时间内执行我们的任务。 Java中多线程定时器Timer 在 Java 中,我们可以使用 Timer 类来实现定时器功能,它是 java.util 中的一个类,在 Quartz 等其它框架出现之前也是常用的定时器实现方式之一。…

    多线程 2023年5月17日
    00
  • 详解PHP服务器如何在有限的资源里最大提升并发能力

    当PHP服务器面对大量用户请求时,如何在有限的资源里提升其并发能力是一个非常关键的问题。下面是一些具体做法以及案例分析,可以帮助提升PHP服务器的并发能力。 1. 改善代码架构 优化代码架构可以有效提高服务器的性能。具体而言,可以针对如下几个方面进行优化。 1.1 精简代码 减少无用的代码和逻辑,缩小代码体积,可以有效减少服务器的负担,提高响应速度。比如,可…

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