简介
Semaphore 信号量是用来限制多个线程访问同一个资源,简单来说就是只能有特定数量的线程同时访问资源。与锁不同的是,锁只允许一个线程访问特定的资源,而信号量是允许指定数量的线程同时访问,可以用来协调多线程。
Semaphore 并不是Java语言所特有的,几乎所有的并发语言都有。信号量模型都是一样的,如下:
由计数器、队列和三个方法所组成。
计数器 :记录还可以有多少个线程来访问资源
等待队列 :将等待资源的线程放入此队列
/**
* Acquires a permit from this semaphore, blocking until one is
* available, or the thread is {@linkplain Thread#interrupt interrupted}.
* 阻塞到获取许可
* <p>Acquires a permit, if one is available and returns immediately,
* reducing the number of available permits by one.
* 获取许可,如果有一个许可是可用的,立即返回并减少数量
* <p>If no permit is available then the current thread becomes
* disabled for thread scheduling purposes and lies dormant until
* one of two things happens:
* <ul>
* <li>Some other thread invokes the {@link #release} method for this
* semaphore and the current thread is next to be assigned a permit; or
* <li>Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread.
* </ul>
* 如果没有许可可用,那么当前线程就会不可线程调度并且休眠,直到:
* 1、其他线程调用了release方法释放了该信号量的许可证,并且当前线程是下一个被分配到许可证的
* 2、其他线程中断了当前线程
* <p>If the current thread:
* <ul>
* <li>has its interrupted status set on entry to this method; or
* <li>is {@linkplain Thread#interrupt interrupted} while waiting
* for a permit,
* </ul>
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* @throws InterruptedException if the current thread is interrupted
*/
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
Limiter
基于 Semaphore 可以很轻松的实现限流,比如同一时间内只能有 5 个线程执行:
private static final int THREAD_COUNT = 100;
private static ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);
private static Semaphore semp = new Semaphore(5);
public static void main(String[] args) {
for (int i = 0; i < THREAD_COUNT; i++) {
semp.acquire();
executorService.execute(()->{
try {
// 如果无法获取许可,将会一直阻塞...
System.out.println("Thread - " + Thread.currentThread().getName() +" sleep ...");
Thread.sleep(1000);
semp.release();
} catch (InterruptedException e) {
// 如果被中断,则会抛出异常
e.printStackTrace();
}finally {
}
});
}
}
也可以基于 Semaphore 对特定的特定的资源进行限流。
public class Limiter {
private Semaphore semaphore = new Semaphore(50);
private Lock lock = new ReentrantLock();
public boolean acquire() {
//lock.lock();
try {
// acquire会阻塞
semaphore.acquire();
return true;
} catch (InterruptedException e) {
// 当前线程被中断时会抛出 InterruptedException
// Ingore it
//e.printStackTrace();
return false;
} finally {
//lock.unlock();
}
}
public void release() {
semaphore.release();
}
public static void main(String[] args) {
Limiter limiter = new Limiter();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
try {
if (limiter.acquire()) {
System.out.println("sleep------>" + Thread.currentThread().getName());
Thread.sleep(10000);
System.out.println("wake------>" + Thread.currentThread().getName());
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
limiter.release();
}
}, "Thread-" + i).start();
}
}
}