多线程高并发篇终于到了高级篇。基础篇和进阶篇主要讲了线程的相关内容以及一些原理分析,最最重要的就是JMM和AQS(AbstractQueuedSynchronizer ),理解了这两个基础,高并发涉及的内容就差不多了。高级篇中涉及一些常用的并发工具类和框架。
信号量,用个很形象的例子来说,就比如我们的安检限行,一次允许通过几个人,当看见安检员的NO PASS的牌子时,就等着吧。
类似的,信号量也需要设置准入数。我们看下源码中信号量的构造方法。在信号量的构造方法中,有两个构造方法。
/** * Creates a {@code Semaphore} with the given number of * permits and nonfair fairness setting. * * @param permits the initial number of permits available. * This value may be negative, in which case releases * must occur before any acquires will be granted. */ public Semaphore(int permits) { sync = new NonfairSync(permits); } /** * Creates a {@code Semaphore} with the given number of * permits and the given fairness setting. * * @param permits the initial number of permits available. * This value may be negative, in which case releases * must occur before any acquires will be granted. * @param fair {@code true} if this semaphore will guarantee * first-in first-out granting of permits under contention, * else {@code false} */ public Semaphore(int permits, boolean fair) { sync = (fair)? new FairSync(permits) : new NonfairSync(permits); }
从构造方法中我们可以看出,信号量的准入数是必传的(准入数规定了一次可执行的线程数),默认使用的是非公平的队列同步器(当然可以根据第二个去设置使用公平的队列同步器)。
1.常用的几个主要方法
我们直接看源码。
/** * Acquires a permit from this semaphore, blocking until one is * available, or the thread is {@linkplain Thread#interrupt interrupted}. * *Acquires a permit, if one is available and returns immediately, * reducing the number of available permits by one. * *
If no permit is available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * one of two things happens: *
-
*
- Some other thread invokes the {@link #release} method for this * semaphore and the current thread is next to be assigned a permit; or *
- Some other thread {@linkplain Thread#interrupt interrupts} * the current thread. *
If the current thread: *
-
*
- has its interrupted status set on entry to this method; or *
- is {@linkplain Thread#interrupt interrupted} while waiting * for a permit, *
Acquires a permit, if one is available and returns immediately, * reducing the number of available permits by one. * *
If no permit is available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * some other thread invokes the {@link #release} method for this * semaphore and the current thread is next to be assigned a permit. * *
If the current thread is {@linkplain Thread#interrupt interrupted} * while waiting for a permit then it will continue to wait, but the * time at which the thread is assigned a permit may change compared to * the time it would have received the permit had no interruption * occurred. When the thread does return from this method its interrupt * status will be set. */ public void acquireUninterruptibly() { sync.acquireShared(1); } /** * Acquires a permit from this semaphore, only if one is available at the * time of invocation. * *
Acquires a permit, if one is available and returns immediately, * with the value {@code true}, * reducing the number of available permits by one. * *
If no permit is available then this method will return * immediately with the value {@code false}. * *
Even when this semaphore has been set to use a * fair ordering policy, a call to {@code tryAcquire()} will * immediately acquire a permit if one is available, whether or not * other threads are currently waiting. * This "barging" behavior can be useful in certain * circumstances, even though it breaks fairness. If you want to honor * the fairness setting, then use * {@link #tryAcquire(long, TimeUnit) tryAcquire(0, TimeUnit.SECONDS) } * which is almost equivalent (it also detects interruption). * * @return {@code true} if a permit was acquired and {@code false} * otherwise */ public boolean tryAcquire() { return sync.nonfairTryAcquireShared(1) >= 0; } /** * Acquires a permit from this semaphore, if one becomes available * within the given waiting time and the current thread has not * been {@linkplain Thread#interrupt interrupted}. * *
Acquires a permit, if one is available and returns immediately, * with the value {@code true}, * reducing the number of available permits by one. * *
If no permit is available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * one of three things happens: *
-
*
- Some other thread invokes the {@link #release} method for this * semaphore and the current thread is next to be assigned a permit; or *
- Some other thread {@linkplain Thread#interrupt interrupts} * the current thread; or *
- The specified waiting time elapses. *
If a permit is acquired then the value {@code true} is returned. * *
If the current thread: *
-
*
- has its interrupted status set on entry to this method; or *
- is {@linkplain Thread#interrupt interrupted} while waiting * to acquire a permit, *
If the specified waiting time elapses then the value {@code false} * is returned. If the time is less than or equal to zero, the method * will not wait at all. * * @param timeout the maximum time to wait for a permit * @param unit the time unit of the {@code timeout} argument * @return {@code true} if a permit was acquired and {@code false} * if the waiting time elapsed before a permit was acquired * @throws InterruptedException if the current thread is interrupted */ public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); } /** * Releases a permit, returning it to the semaphore. * *
Releases a permit, increasing the number of available permits by * one. If any threads are trying to acquire a permit, then one is * selected and given the permit that was just released. That thread * is (re)enabled for thread scheduling purposes. * *
There is no requirement that a thread that releases a permit must * have acquired that permit by calling {@link #acquire}. * Correct usage of a semaphore is established by programming convention * in the application. */ public void release() { sync.releaseShared(1); }
在信号量类的方法中,实现原理就是AQS,因此请参考AQS相关帖子内容。
我们只介绍下功能。
①acquire():该方法获取一个准入许可,若无法获得,线程等待,直到有线程释放一个许可或者当前线程被中断,这也表达了另外一层意思--该方法是可以响应中断的。
②acquireUninterruptibly():该方法获取一个准入许可,若无法获得,线程等待,直到有线程释放一个许可,不同的是该方法不响应中断。
③tryAcquire():尝试获取一个许可,如果成功,返回true,如果失败,返回false,并且会立即返回,不等待。
④tryAcquire(long timeout, TimeUnit unit):在给定的时间内尝试获取一个许可,如果成功,返回true,如果失败,返回false。
⑤release():释放当前线程占用的许可,供其他等待许可的线程使用。
举例:
public class SemaphoreDemo implements Runnable { final Semaphore semp = new Semaphore(5); @Override public void run() { try { semp.acquire(); //模拟处理业务逻辑 Thread.sleep(2000); System.out.println(Thread.currentThread().getId()+" done!"); semp.release(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) { ExecutorService exce = Executors.newFixedThreadPool(20); final SemaphoreDemo demo = new SemaphoreDemo(); for (int i = 0; i < 20; i++) { exce.submit(demo); } } }
从执行结果我们可以看到,系统以5个线程一组为单位,依次输出,也就是说同时只允许5个线程同时进入模拟业务逻辑处。这里要注意,如果发生信号泄露(就是在申请了准入许可后,没有释放),那么可用的准入许可越来越少,直至都被占用。