Это вторая статья одновременных нитей. В первой статье мы проанализированыCountDownLatch
Связанный контент, вы можете обратиться к
Узнайте об использовании и исходном коде CountDownLatch в одной статье!
Итак, в этой статье мы продолжим рассказывать вам о второй статье в категории инструментов параллелизма --- Semaphore.
Познакомьтесь с семафором
Что такое семафор
Общий перевод семафора信号量
, который также является инструментом синхронизации потоков, который в основном используется несколькими потоками для выполнения параллельных операций над общими ресурсами. он представляет собой许可
Концепция того, разрешать ли нескольким потокам работать с одним и тем же ресурсом, разрешена.Используя семафор, вы можете контролировать количество потоков, которые одновременно обращаются к ресурсам.
Сценарии использования семафора
Сценарии использования семафоров в основном используются для流量控制
, таких как подключения к базе данных, количество подключений к базе данных, используемых одновременно, будет ограничено, а количество подключений к базе данных не может превышать определенного числа.Когда количество подключений достигает предела, более поздние потоки могут только стоять в очереди и ждать для предыдущих потоков, чтобы освободить соединение с базой данных, прежде чем они смогут получить соединение с базой данных.
Другой пример — светофор на автомагистрали, когда горит зеленый свет, могут проехать только 100 автомобилей, а когда горит красный свет, проехать нельзя ни одному транспортному средству.
Другим примером является сцена с парковкой. На парковке ограничено количество парковочных мест, и сколько автомобилей может быть размещено одновременно. После того, как парковочные места заполнены, только автомобили внутри могут покинуть парковку до автомобили вне стоянки могут въехать.
Использование семафора
Давайте смоделируем бизнес-сценарий парковки: перед въездом на парковку будет табличка, показывающая, сколько есть парковочных мест, когда парковочное место равно 0, вы не можете въехать на парковку, когда парковочное место не равно 0. , Транспортные средства будут допущены только на стоянку. Итак, у парковки есть несколько ключевых факторов: общая вместимость парковки, при въезде автомобиля, общая вместимость парковки - 1, при выезде автомобиля, общая вместимость + 1, при недостатке парковки, Транспортные средства могут ждать только за пределами парковки.
public class CarParking {
private static Semaphore semaphore = new Semaphore(10);
public static void main(String[] args){
for(int i = 0;i< 100;i++){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("欢迎 " + Thread.currentThread().getName() + " 来到停车场");
// 判断是否允许停车
if(semaphore.availablePermits() == 0) {
System.out.println("车位不足,请耐心等待");
}
try {
// 尝试获取
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 进入停车场");
Thread.sleep(new Random().nextInt(10000));// 模拟车辆在停车场停留的时间
System.out.println(Thread.currentThread().getName() + " 驶出停车场");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, i + "号车");
thread.start();
}
}
}
В приведенном выше коде мы задаем начальную емкость Semaphore, которая составляет всего 10 парковочных мест, мы используем эти 10 парковочных мест для управления потоком из 100 автомобилей, поэтому результат очень похож на то, что мы ожидали, то есть большинство автомобилей все ждут. Но в то же время некоторым автомобилям все же разрешен въезд на парковку.Транспортные средства, которые въезжают на парковку, будут занимать парковочное место с semaphore.acquire, а когда они выезжают с парковки, они будут semaphore.release отказываться от парковки. парковочное место, позволяющее снова въехать следующим автомобилям.
Модель семафора Семафор
Хотя приведенный выше код относительно прост, он позволяет нам понять модель семафора.五脏六腑
. Ниже представлена модель семафора:
Чтобы объяснить семафор, семафор имеет начальную емкость, эта начальная емкость является семафором, который может разрешить семафор. После вызова метода получения в семафоре емкость семафора равна -1, а после вызова метода освобождения емкость семафора равна +1. Во время этого процесса счетчик отслеживает изменение количества семафоров. пока трафик не превысит пропускную способность семафора, избыточный трафик будет помещаться в очередь ожидания для постановки в очередь. Подождите, пока емкость семафора позволит это, прежде чем снова войти.
Трафик, контролируемый Semaphore, на самом деле является потоком, потому что основным объектом исследования инструментов параллелизма является поток.
Его рабочий процесс выглядит следующим образом
Эту картинку должно быть легко понять, поэтому я не буду объяснять ее здесь слишком подробно.
Глубокое понимание семафора
После понимания основного использования семафора и модели семафора нам все еще нужно поговорить с вами о деталях семафора из исходного кода, потому что основная цель моей статьи - позволить моим читателямУзнайте ххх, посмотри на это достаточноЭто статья, которую я написал в погоне за хорошим человеком, немногословным, сказал источник, чтобы подняться!
Основные свойства семафора
В Semaphore есть только одно свойство
private final Sync sync;
Синхронизация — это реализация синхронизации Semaphore Способ, которым Semaphore гарантирует безопасность потоков, похож на ReentrantLock и CountDownLatch, которые унаследованы от реализации AQS. Точно так же эта синхронизация также наследуется отAbstractQueuedSynchronizer
Переменная , то есть говоря о семафоре, не может обойти AQS, поэтому AQS действительно важен.
Справедливость и несправедливость Semaphore
Итак, давайте зайдем внутрь Sync и посмотрим, какие методы он реализует.
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, которая вызывается внутреннеsetState
И передавая разрешения, мы знаем, что State в AQS на самом деле является значением состояния синхронизации, а разрешения Semaphore представляют количество разрешений.
getPermits фактически вызывает метод getState для получения значения состояния синхронизации потока. Последний метод nonfairTryAcquireShared фактически создает вызов tryAcquireShared в NonfairSync в Semaphore.
Что здесь нужно упомянутьNonfairSync
, кроме NonfairSync есть FairSync? Проверьте исходный код JDK и убедитесь, что это так.
Так что же здесь означают FairSync и NonfairSync? Почему два класса?
Фактически, семафор, например, ReentrantLock, также имеет два вида «ярмарки» и «несправедливого». По умолчанию семафор - недобросовестный семафор
Нечестный семафор означает, что он не гарантирует, что поток получит порядок разрешения, семафор, прежде чем поток будет ждать, чтобы получить вызывающий поток, получает лицензию, лицензия имеет этот поток, который будет автоматически помещен в свой собственный поток, ожидающий начала очереди.
Когда этот параметр имеет значение true, Semaphore гарантирует, что любые вызовы для получения будут получать разрешения в порядке поступления и получения.
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
// 获取同步状态值
int available = getState();
// state 的值 - 当前线程需要获取的信号量(通常默认是 -1),只有
// remaining > 0 才表示可以获取。
int remaining = available - acquires;
// 先判断是否小于 0 ,如果小于 0 则表示无法获取,如果是正数
// 就需要使用 CAS 判断内存值和同步状态值是否一致,然后更新为同步状态值 - 1
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
Как видно из приведенного выше сравнения исходного кода, самая большая разница между NonfairSync и FairSync заключается в том, чтоtryAcquireShared
разница методов.
В версии NonfairSync не имеет значения, есть ли разрешение на постановку в очередь в текущей очереди ожидания, она напрямую оценивает количество разрешений на сигнал и осуществимость метода CAS.
В версии FairSync он сначала определит, есть ли лицензия на очередь, и если она есть, то не сможет получить ее напрямую.
В это время некоторые читатели могут спросить, вы сказали выше, что разница между честностью и несправедливостью всегда была направлена на метод приобретения, Почему сейчас основное различие между ними двумя?
tryAcquireShared
метод?
Не волнуйтесь, приступимacquire
Как узнать
Видно, что в методе Acquire будет вызываться метод tryAcquireShared, и по возвращаемому значению будет решаться, вызывать ли его.doAcquireSharedInterruptibly
метод, для более подробного анализа использования doAcquireSharedInterruptably обратитесь к этой статье читателя
Узнайте об использовании и исходном коде CountDownLatch в одной статье!
Здесь следует отметить, что метод take является блокирующим, а метод tryAcquire — нет.
Это означает, что если лицензия не получена путем вызова метода Acquire, Semaphore будет блокироваться до тех пор, пока лицензия не станет доступной. Метод tryAcquire сразу вернет false, если разрешение не получено.
Также обратите внимание здесьacquireUninterruptibly
метод, другие методы, связанные с получением, являются либо неблокирующими, либо блокирующими и прерываемыми, а методAcquireUninterruptably не только постоянно ждет без разрешения, но и не прерывается.При использовании этого метода вам нужно обратить внимание, этот метод легко появляется в крупномасштабной блокировке потока процесс Java выглядит приостановленным.
Существует лицензия на выпуск, соответствующая лицензии на приобретение, но лицензия на выпуск не различает, является ли это честным выпуском или недобросовестным выпуском. В любом случае лицензия передается семафору, и количество лицензий на одном и том же семафоре увеличивается.
После вызова tryReleaseShared на приведенном выше рисунке, чтобы определить, может ли он быть выпущен, AQS будет вызван снова.releasedShared
метод освобождения.
Вышеупомянутый процесс выпуска выпускает только одну лицензию, кроме того, может быть выпущено несколько лицензий.
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
}
Процесс выпуска ReleaseShared позже соответствует описанному выше процессу выпуска.
Другие методы семафора
В дополнение к основным методам, связанным с получением и освобождением, описанным выше, нам также необходимо узнать о других методах Semaphore. Есть несколько других методов Semaphore, только следующие
drainPermits: получает и возвращает все сразу доступные лицензии, что фактически эквивалентно использованию метода CAS для установки значения памяти в 0
reducePermits:иnonfairTryAcquireShared
Метод аналогичен, за исключением того, что nonfairTryAcquireShared использует CAS, чтобы сделать значение памяти +1, а reducePermits делает значение памяти -1.
isFair: Конкурс семафор за лицензию - это использование справедливого или несправедливости, для достижения внутренней переписки является Fairsync и Nonfairsync.
hasQueuedThreads: заблокированы ли какие-либо потоки в настоящее время из-за получения разрешения семафора.
getQueuedThreads: возвращает коллекцию потоков, ожидающих получения разрешения.
getQueueLength: получить количество потоков, находящихся в очереди и перешедших в состояние блокировки.
У меня самого было шесть PDF-файлов, и вся сеть распространилась более чем на 10w +. После поиска «programmer cxuan» в WeChat и после официального аккаунта я ответил cxuan в фоновом режиме и получил все PDF-файлы.Эти PDF-файлы следующие