前几篇分析了一下AQS的原理和实现。这篇拿Semaphore信号量做样例看看AQS实际是怎样使用的。

Semaphore表示了一种能够同一时候有多个线程进入临界区的同步器,它维护了一个状态表示可用的票据,仅仅有拿到了票据的线程尽能够进入临界区,否则就等待。直到获得释放出的票据。

Semaphore经常使用在资源池中来管理资源。当状态仅仅有1个0两个值时,它退化成了一个相互排斥的同步器。类似锁。

以下来看看Semaphore的代码。

它维护了一个内部类Sync来继承AQS,定制tryXXX方法来使用AQS。

我们之前提到过AQS支持独占和共享两种模式,Semaphore明显就是共享模式。它支持多个线程能够同一时候进入临界区。所以Sync扩展了Shared相关的方法。

能够看到Sync的主要操作都是对状态的无锁改动,它不须要处理AQS队列相关的操作。在聊聊高并发(二十四)解析java.util.concurrent各个组件(六) 深入理解AQS(四) 我们说了AQS提供了tryXXX接口给子类扩展,相当于给子类一个机会,能够自己处理状态,决定是否入同步队列。

1. nonfailTryAcquireShared()非公平的tryAcquire,它立马改动了票据状态,而不须要管是否有先来的线程正在等待,而一旦有可用的票据,就直接获得了锁,不须要进入AQS的队列等待同步。

2. tryReleaseShared()方法负责释放共享状态的资源,它仅仅改动了票据状态。由AQS的releaseShared()方法来负责唤醒在AQS队列等待的线程

3. reducePermits()和drainPermits()方法都是直接改动了状态,从而限制可用的资源

abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L; Sync(int permits) {
setState(permits);
} final int getPermits() {
return getState();
} final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
} protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
} final void reducePermits(int reductions) {
for (;;) {
int current = getState();
int next = current - reductions;
if (next > current) // underflow
throw new Error("Permit count underflow");
if (compareAndSetState(current, next))
return;
}
} final int drainPermits() {
for (;;) {
int current = getState();
if (current == 0 || compareAndSetState(current, 0))
return current;
}
}
}

Sync也是一个抽象类,详细的实现是NonfailSync和FairSync。代表了非公平实现和公平实现。在上一篇已经提到,所谓的非公平仅仅是说在获取资源时开了一个口子。能够让后来的线程不须要管在AQS队列中的先来的线程来获取资源。而一旦获取失败,就得进入AQS队列等待,而AQS队列是先来先服务的FIFO队列。

能够看到,NonfailSync和FairSync仅仅是在tryAcquireShared方法的实现上不同,其它都是一样的。

/**
* NonFair version
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -2694183684443567898L; NonfairSync(int permits) {
super(permits);
} protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
} /**
* Fair version
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = 2014338818796000944L; FairSync(int permits) {
super(permits);
} protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
}

再来看看Semaphore自己提供的方法,

1.支持可中断和不可中断的获取/释放

2.支持限时获取

3.支持tryXX获取/释放

4. 支持同一时候获取/释放多个资源

能够看到Semaphore的实现都是基于AQS的方法来作的,单个资源的获取/释放操作都是请求1个资源,所以參数传递的是1,多个资源获取传递了一个int个数。

public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
} public void acquireUninterruptibly() {
        sync.acquireShared(1);
    } public boolean tryAcquire() {
        return sync.nonfairTryAcquireShared(1) >= 0;
    } public boolean tryAcquire(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    } public void release() {
        sync.releaseShared(1);
    } public void acquire(int permits) throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireSharedInterruptibly(permits);
    } public void acquireUninterruptibly(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireShared(permits);
    } public boolean tryAcquire(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        return sync.nonfairTryAcquireShared(permits) >= 0;
    } public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
        throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
    } public void release(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.releaseShared(permits);
    }

以下用一个实例来測试一下Semaphore的功能。

1. 创建一个有两个票据的Semaphore

2. 创建20个线程来竞争运行race()方法

3. 在race()方法里先打印一句等待获取资源的话,再获取资源,获得资源后打印一句话,最后释放资源,释放资源前打印一句话

package com.lock.test;

import java.util.concurrent.Semaphore;

public class SemaphoreUsecase {
private Semaphore semaphore = new Semaphore(2); public void race(){
System.out.println("Thread " + Thread.currentThread().getName() + " is waiting the resource");
semaphore.acquireUninterruptibly();
try{
System.out.println("Thread " + Thread.currentThread().getName() + " got the resource");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}finally{
System.out.println("Thread " + Thread.currentThread().getName() + " is releasing the resource");
semaphore.release();
}
} public static void main(String[] args){
final SemaphoreUsecase usecase = new SemaphoreUsecase(); for(int i = 0; i < 10; i++){
Thread t = new Thread(new Runnable(){ @Override
public void run() {
usecase.race();
} }, String.valueOf(i));
t.start();
}
}
}

測试结果:

能够看到先来的两个线程先获得了资源,后来的线程都在等待,当有线程释放资源之后,等待的线程才会去获得资源,直到都获得/释放资源

Thread 0 is waiting the resource
Thread 0 got the resource
Thread 2 is waiting the resource
Thread 2 got the resource
Thread 1 is waiting the resource
Thread 4 is waiting the resource
Thread 3 is waiting the resource
Thread 5 is waiting the resource
Thread 6 is waiting the resource
Thread 7 is waiting the resource
Thread 8 is waiting the resource
Thread 9 is waiting the resource
Thread 2 is releasing the resource
Thread 0 is releasing the resource
Thread 1 got the resource
Thread 4 got the resource
Thread 1 is releasing the resource
Thread 4 is releasing the resource
Thread 3 got the resource
Thread 5 got the resource
Thread 3 is releasing the resource
Thread 5 is releasing the resource
Thread 6 got the resource
Thread 7 got the resource
Thread 7 is releasing the resource
Thread 6 is releasing the resource
Thread 8 got the resource
Thread 9 got the resource
Thread 8 is releasing the resource
Thread 9 is releasing the resource

聊聊高并发(二十五)解析java.util.concurrent各个组件(七) 理解Semaphore的更多相关文章

  1. 聊聊高并发(二十)解析java.util.concurrent各个组件(二) 12个原子变量相关类

    这篇说说java.util.concurrent.atomic包里的类,总共12个.网上有非常多文章解析这几个类.这里挑些重点说说. watermark/2/text/aHR0cDovL2Jsb2cu ...

  2. 谈论高并发(三十)解析java.util.concurrent各种组件(十二) 认识CyclicBarrier栅栏

    这次谈话CyclicBarrier栅栏,如可以从它的名字可以看出,它是可重复使用. 它的功能和CountDownLatch类别似,也让一组线程等待,然后开始往下跑起来.但也有在两者之间有一些差别 1. ...

  3. 聊聊高并发(四十)解析java.util.concurrent各个组件(十六) ThreadPoolExecutor源代码分析

    ThreadPoolExecutor是Executor运行框架最重要的一个实现类.提供了线程池管理和任务管理是两个最主要的能力.这篇通过分析ThreadPoolExecutor的源代码来看看怎样设计和 ...

  4. 聊聊高并发(二十九)解析java.util.concurrent各个组件(十一) 再看看ReentrantReadWriteLock可重入读-写锁

    上一篇聊聊高并发(二十八)解析java.util.concurrent各个组件(十) 理解ReentrantReadWriteLock可重入读-写锁 讲了可重入读写锁的基本情况和基本的方法,显示了怎样 ...

  5. 聊聊高并发(二十八)解析java.util.concurrent各个组件(十) 理解ReentrantReadWriteLock可重入读-写锁

    这篇讲讲ReentrantReadWriteLock可重入读写锁,它不仅是读写锁的实现,而且支持可重入性. 聊聊高并发(十五)实现一个简单的读-写锁(共享-排他锁) 这篇讲了怎样模拟一个读写锁. 可重 ...

  6. 高并发第十单:J.U.C AQS(AbstractQueuedSynchronizer) 组件:CountDownLatch. CyclicBarrier .Semaphore

    这里有一篇介绍AQS的文章 非常好: Java并发之AQS详解 AQS全名:AbstractQueuedSynchronizer,是并发容器J.U.C(java.lang.concurrent)下lo ...

  7. 聊聊高并发(三十九)解析java.util.concurrent各个组件(十五) 理解ExecutorService接口的设计

    上一篇讲了Executor接口的设计,目的是将任务的运行和任务的提交解耦.能够隐藏任务的运行策略.这篇说说ExecutorService接口.它扩展了Executor接口,对Executor的生命周期 ...

  8. 聊聊高并发(二十四)解析java.util.concurrent各个组件(六) 深入理解AQS(四)

    近期总体过了下AQS的结构.也在网上看了一些讲AQS的文章,大部分的文章都是泛泛而谈.又一次看了下AQS的代码,把一些新的要点拿出来说一说. AQS是一个管程.提供了一个主要的同步器的能力,包括了一个 ...

  9. 聊聊高并发(四十四)解析java.util.concurrent各个组件(二十) Executors工厂类

    Executor框架为了更方便使用,提供了Executors这个工厂类.通过一系列的静态工厂方法.能够高速地创建对应的Executor实例. 仅仅有一个nThreads參数的newFixedThrea ...

随机推荐

  1. Unity cg vertex and fragment shaders(一)

    cg片段 Cg程序片段写CGPROGRAM和ENDCG之间 开始时的片段可以作为#pragma语句编译指令 Pass { // ... the usual pass state setup ... C ...

  2. ExtJs005继承

    Ext.onReady(function () { //extend 继承 Ext.define('Person', { config: { name: 'aaa' }, //给当前定义的类加一个构造 ...

  3. C#文本文件读写

    算机在最初只支持ASCII编码,但是后来为了支持其他语言中的字符(比如汉字)以及一些特殊字符(比如€),就引入了Unicode字符集.基于Unicode字符集的编码方式有很多,比如UTF-.UTF-. ...

  4. LNMP 基于域名的虚拟主机配置 (Centos5.6)

    . . server { listen ; #listen [::]: default_server ipv6only=on; server_name www.blog.com; index inde ...

  5. 12-C语言字符串

    目录: 一.字符串 二.字符串输入输出函数 三.指针数组(字符串数组) 回到顶部 一.字符串 1 一组字符数组,以数组的首地址开始,以ASC码的'\0'结束. 2 字符串与普通数组的区别:普通数组没有 ...

  6. web乱码问题

    String data = "不见青山"; ServletOutputStream sos = response.getOutputStream(); byte b[] = dat ...

  7. mybatis优化配置

    在src下建立db.properties driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis name=root ...

  8. HDU 1870 愚人节的礼物

    题解:简单的数括号问题…… #include <cstdio> char s[1005]; int main(){ while(scanf("%s",s)!=EOF){ ...

  9. 通过自定义window来实现提示框效果

    #import <UIKit/UIKit.h>#import <Foundation/Foundation.h> @interface ZMStatuBarHud : NSOb ...

  10. java软件工程师成长过程的学习

    第一阶段:Java基础,包括java语法,面向对象特征,常见API,集合框架: *第二阶段:java界面编程,包括AWT,事件机制,SWING,这个部分也可以跳过,用的时候再看都能来及: *第三阶段: ...