AQS是Java改进的一种底层同步工具类。这篇文章涉及AQS资源共享的方式,角色状态,定制同步器的方法,CountDownLatch,区分Semaphore、Semaphore和RateLimiterCyclicBarrier的几个方面对Java并发工具类进行了剖析。
AQS维护一个volatileintstate(代表共享资源)和一个FIFO线程等待队列(多线程争用资源被阻塞时将进入该队列),用于后续调度。另外,对于Condition的处理,可能还有一个或多个Condition单向链表,该链表不是必需的,可能不存在。
1.AQS如何共享资源。
Exclusive(只有一个线程可以执行,比如ReentrantLock)
共享(多个线程可以同时执行,比如Semaphore/CountDownLatch)
2.state的作用。
在ReentrantLock的例子中,状态被初始化为0,这表明状态是没有锁定的。当A线程lock()时,调用tryAcquire()对该锁进行排他,然后将state+1。然后,当其他线程再tryAcquire()()时失败,直到A线程unlock()()到state=0(即释放锁定),其他线程才有机会获得锁。自然地,在释放锁之前,A线程本身就可以获得这个锁(state累积),这就是可重入的概念。但是,请注意,获取的次数将被释放多少次,以便确保state可以返回到0状态。
例如CountDownLatch,任务被分成N个线程来执行,同时初始化成N(注意N要和线程的数目一致)。这个子线程是以并行方式执行的,在每个子线程执行完countDown()()之后,state将CAS减去1。直到所有子线程执行完毕(即state=0),将unpark()调用主线程,然后调用主线程将从await()函数返回,并继续执行后续操作。
3.同步器的定制方法。
特定线程等待队列的维护(例如获取资源失败进入/唤醒出队等),AQS已经在最上面实现了。当定制的同步器实现时,主要实现一些方法:
isHeldExclusively():线程是独占资源吗?您只需要使用condition来实现它。
tryAcquire(int):唯一的。试图获得资源,成功后返回true,失败返回false。
tryRelease(int):专有的。试图释放资源,成功后返回true,失败后返回false。
tryAcquireShared(int):如何共享。试图获得资源。负的数字表示失败;0代表成功,但是没有剩余的可用资源;正的数字是成功的,还有剩余的资源。
tryReleaseShared(int):如何共享。试图释放资源,如果唤醒后续的待结点之后返回true,否则返回false。
4.CountDownLatch。
CountDownLatch是通过计数器实现的,它的初始值是线程的数目。无论何时,当线程完成其自身的任务时,计数器值将减少1。随着计数器的值达到0时,它指示所有线程都已经完成任务,然后等待的线程可以继续执行任务。
通用应用场景。
多线程进行资源初始化,主线程将暂停等待初始化结束;在所有线程初始化结束之后,在所有线程初始化结束之后,都进行一次countDown,然后在此期间继续执行主线程。
5.Semaphore
Semaphore可以控制某一资源可以同时访问的数量,通过acquire()获得了授权,如果不等待,而release()释放了许可。单一信号量的Semaphore对象可以实现互斥,可以通过一个线程获得锁,然后通过另一个线程释放锁,这种方法可以应用到某些情况下进行死锁恢复。
通用的应用场景。
Semaphore可用于流量控制,限制对某些资源(物理或逻辑)的访问,特别是对公共资源有限的应用场景,如数据库连接。
6.Semaphore与RateLimiter的不同。
Semaphore:这个功能将只能限制只获取信号的线程执行,其它线程必须等待。您可以设置N个信号,以便同时执行N个线程。请注意,其他线程只是挂起,并且限制线程数目以限制其流量。
RateLimiter:Guava的流量限制类,基于标记桶算法实现。它的功能是限制在一秒内只可以有N个线程执行,超过时只能等待下一秒。请注意N的类型为double。以速率为依据的限制流量。
7.CyclicBarrier。
CyclicBarrier可以让一些线程重复地聚集到栅栏位置上。await方法是在线程达到栅栏位置时被调用的,这个方法会阻塞,直到所有的线程都到达栅栏位置。假如所有的线程都达到了栅栏位置,那么栅栏就会打开,这时所有的线程都会被释放,栅栏也会重新设置为下一次使用的原始计数。
通用的应用场景。
对多线程数据进行计算,最后合并计算结果的方案。每一个parter负责一部分计算,最后完成数据汇总。
上面是对AQSJava并发工具类的一个简单介绍,不知道大家都了解了吗?