Semaphore - это класс, обычно используемый в пакете JUC. Это приложение режима совместного использования AQS. Это может позволить нескольким потокам работать на общих ресурсах одновременно и может эффективно контролировать количество параллелизма. Это может достичь хорошего управления трафиком. Semaphore предоставляет концепцию лицензии, которую можно рассматривать как билет на автобус. Только те, кто успешно получает билет, могут попасть в автобус. Существует определенное количество билетов, и их невозможно выпустить без ограничений, что приведет к перегрузке автобуса. Поэтому, когда билет выпущен (автобус заполнен), другие могут ждать только следующего поезда. Если кто -то выйдет из автобуса на полпути, его позиция будет бесплатной, поэтому, если другие захотят сесть в автобус в это время, он может снова получить билеты. Различные пулы могут быть реализованы с использованием семафора. В конце этой статьи мы напишем простой пул подключений к базе данных. Во -первых, давайте посмотрим на конструктор Semaphore.
// Конструктор 1Public Semaphore (int разрешает) {sync = new nonfairsync (разрешения);} // Конструктор 2Public Semaphore (int разрешает, логическая ярмарка) {sync = fair? New Fairsync (разрешения): новый nonfairsync (разрешения);}Semaphore предоставляет два конструктора без параметров, но не предоставляется конструкторов без параметров. Оба конструктора должны проходить в начальном количестве лицензий. Семфор, построенный с использованием конструктора 1, будет получен не FAir при получении лицензии. Использование конструктора 2 может указать метод получения лицензии через параметры (справедливые или несправедливые). Semaphore в основном предоставляет два типа API для внешнего мира, получая лицензии и освобождая лицензии. По умолчанию является получение и освобождение одной лицензии, а параметры также могут быть переданы для получения и выпуска нескольких лицензий одновременно. В этой статье мы будем говорить только о ситуации получения и выпуска одной лицензии каждый раз.
1. Получить лицензию
// Получить лицензию (прерывание ответа прерывания) public void accire () throws urruptedException {sync.acquiresharedintertible (1);} // Получить лицензию (не реагируя на прерывание) public void accireuninterbultable () {sync.Acquireshared (1);} // Попробуйте получить лицензию (нефус sync.nonfairtryacquireshared (1)> = 0;} // Попробуйте получить лицензию (длительный тайм -аут, единица TimeUnit), бросает прерыванияПриведенный выше API - это операция по приобретению лицензий по умолчанию, предоставленная Semaphore. Получение только по одной лицензии за раз также является общей ситуацией в реальной жизни. В дополнение к прямому выбору, он также обеспечивает попытку извлечения. Прямая операция выбора может заблокировать потока после сбоя, в то время как попытка извлечь не будет. Следует также отметить, что метод TryAcquire используется, чтобы попытаться получить его недобросовестным образом. То, что мы часто используем в обычное время, так это получить лицензию. Давайте посмотрим, как это получено. Вы можете видеть, что метод приобретения напрямую вызывает Sync.AcquireSharedEntertaill (1). Этот метод является методом в AQS. Однажды мы говорили о статьях серии исходного кода AQS. Давайте рассмотрим это снова.
// Приобретение блокировки в режиме прерывания (общий режим) публичный окончательный void accireSharedErraintable (int arg) бросает прерванные перерывы {// сначала определить, прерывается ли поток, если это так, добавьте исключение, если (Think.Eruprupted ()) {Throw new urruptEdException (); } // 1. Попробуйте приобрести Lock if (tryAcquireShared (arg) <0) {// 2. Если приобретение не удается, введите метод DoacquiresharedErenterbully (arg); }}Первый метод AccireSharedEructable - это вызвать метод TryAcquireShared, чтобы попытаться получить. TryAcquireshared - это абстрактный метод в AQS. Два полученных класса Fairsync и Nonfairsync реализуют логику этого метода. FairSync реализует логику справедливого приобретения, в то время как нефарсинк реализует логику приобретения без FAIR.
Abstract Static Class Sync Extends AbstractQueudSynchronizer {// Попробуйте получить окончательный int non -fairtryacquireshared (int приобретает) {for (;;) {// получить доступные лицензии int доступны = getState (); // Получить оставшиеся лицензии int resing = доступно - приобретать; // 1. Если остаться меньше 0, верните оставшиеся непосредственно // 2. Если остаться больше 0, сначала обновите статус синхронизации, а затем верните оставшиеся, если (оставшиеся <0 || CompareandSetState (доступно, оставшееся)) {return storing; }}}} // nonfairsync static final class nonfairsync extends sync {private static final long serialversionuid = -2694183684443567898l; Nonfairsync (int разрешает) {super (разрешения); } // Попробуйте получить лицензию, защищенную int tryAcquiReshared (int приобретает) {return non -fairtryAcquireShared (приобретает); }} // Справедливый синхронизатор статический окончательный класс FairSync Extends Sync {Private Static Final Long SerialVersionUID = 2014338818796000944L; Fairsync (int разрешает) {super (разрешения); } // Попробуйте получить лицензию, защищенную int tryAcquiReshared (int quiens) {for (;;) {// судить, есть ли кто -нибудь перед очередью синхронизации, если (hasqueudpredpredecessors ()) {// Если есть что -то, возврат -1, что указывает на то, что попытка получить неудачный возврат -1; } // Получить доступные лицензии int доступны = getState (); // Получить оставшиеся лицензии int resing = доступно - приобретать; // 1. Если остаться меньше 0, вернитесь непосредственно к оставшимся // 2. Если остаться больше 0, статус синхронизации будет сначала обновлена, а затем возвращено в оставшуюся IF (оставшиеся <0 || CompareandSetState (доступно, оставшееся)) {return oraining; }}}}Здесь следует отметить, что метод TryAcquireshed of non -fairsync непосредственно называет метод NonfairtryAcquireshed, который находится в синхронизации родительского класса. Логика блокировки приобретения, не связанного с F-FIAR, состоит в том, чтобы сначала извлечь текущее состояние синхронизации (синхронное состояние представляет количество лицензий), вычитая параметр текущего состояния синхронизации. Если результат не меньше 0, доказано, что есть все еще имеющиеся лицензии, то значение состояния синхронизации напрямую обновляется с использованием операции CAS, и, наконец, значение результата будет возвращено независимо от того, является ли результат меньше 0. Здесь мы должны понять значение возврата значения обратного метода TryAcquireShared. Возвращение отрицательного числа означает, что сбое не удалось, ноль означает, что текущий поток успешно получен, но последующий поток больше не может быть получен, а положительное число означает, что текущий поток успешно получен, а также можно получить последующий поток. Давайте посмотрим на код метода AcquireSharedEructable.
// Приобретение блокировки в прерываемом режиме (общий режим) публичный окончательный void acpiresharedErraintable (int arg) бросает прерванные перерывы {// сначала определить, прерывается ли поток, если это так, добавьте исключение, если (Thread.Eruprupted ()) {Throw new urruptEdException (); } // 1. Попробуйте получить отрицательное число блокировки //: указывает на то, что сбоя сбора // нулевое значение: указывает на то, что текущий поток успешно получен, но последующий поток больше не может получить // положительное число: указывает, что текущий поток успешно получен, а последующий поток также может получить успех, если (TryAcquireshared (arg) <0) {// 2. Если приобретение не удается, введите метод DoacquiresharedErenterbully (arg); }}Если оставшееся возвращение меньше 0, это означает, что приобретение не удалось. Следовательно, TryAcquireshared (arg) <0 это правда, поэтому метод DoacquiResharedEruptable будет называться следующим. Когда мы поговорили о AQS, он завершит текущий поток в узел и поместит его в хвост очереди синхронизации, и можно привести к приостановке потока. Это также причина, по которой потоки будут стоять в очереди и блокировать, оставаясь меньше 0. Если возвращенные оставшиеся> = 0 означает, что текущий поток был успешно получен. Следовательно, TryAcquireshared (arg) <0 - это флаза, поэтому метод DoacquiresharedEruptable больше не будет вызван для блокировки текущего потока. Вышеуказанное - вся логика несправедливого приобретения. При справедливом приобретении вам нужно только вызвать метод Hasqueudpredecessors, прежде чем определить, находится ли кто -то в очереди в очереди синхронизации. Если это так, возврат -1 непосредственно указывает, что приобретение не удалось, в противном случае следующие шаги продолжаются как несправедливое приобретение.
2. Выпустите лицензию
// Выпуск лицензии public void Release () {sync.ReleaseShared (1);}Вызов метода выпуска должен выпустить лицензию. Его операция очень проста, поэтому мы называем метод AQS с выпуском. Давайте посмотрим на этот метод.
// Выпуск операции блокировки (общий режим) Публичный окончательный логический релиз (int arg) {// 1. Попробуйте выпустить блокировку if (tryReleaseShared (arg)) {// 2. Если релиз успешно, разбудите другие потоки DoreLeaseShared (); вернуть истину; } вернуть false;}Способный метод AQS сначала вызывает метод TryReleaseSeSeared, чтобы попытаться выпустить блокировку. Логика реализации этого метода находится в синхронизации подкласса.
Abstract Static Class Sync Extends AbstractQueudSynchronizer {... // Попробуйте выпустить операцию, защищенную окончательную логическую, TRYRELEASESHARED (int leleases) {for (;;) {// Получить текущее состояние синхронизации int current = getState (); // плюс современное состояние синхронизации следующим образом int next = current + lepleses; // Если результат добавления меньше текущего состояния синхронизации, будет сообщена ошибка, если (следующий <ток) {бросить новую ошибку («максимальное количество разрешений превышен»); } // Обновление значения состояния синхронизации в режиме CAS и вернуть true, если обновление успешно, иначе продолжайте цикл if (CompareandState (current, Next)) {return true; }}} ...}Вы можете видеть, что метод TryReLeaseShared использует для спинки для цикла. Сначала получите состояние синхронизации, добавьте входящие параметры, а затем обновите состояние синхронизации в CAS. Если обновление успешно, верните True и выпрыгните из метода. В противном случае петля будет продолжаться до тех пор, пока он не станет успешным. Это процесс семафора, освобождающего лицензию.
3. Напишите пул соединений вручную
Код семафора не очень сложный. Обычно используемая операция заключается в получении и выпуске лицензии. Логика реализации этих операций относительно проста, но это не препятствует широко распространенному применению семафора. Далее мы будем использовать Semaphore для реализации простого пула соединений базы данных. В этом примере мы надеемся, что читатели смогут более глубоко понять использование семафора.
открытый класс ConnectPool {// Размер пула соединений Private Int Size; // Коллекция подключения к базе данных Private Connect [] Connects; // Статус подключения флаг частный логический [] connectflag; // оставшееся количество доступных соединений частные летучие int доступны; // семафор частный семифор семафор; // Constructor public connectPool (int size) {this.size = size; this.vailable = size; Semaphore = новый семафор (размер, верно); connects = new Connect [size]; ConnectFlag = новый логический [размер]; initConnects (); } // Инициализировать соединение private void initConnects () {// Сгенерировать указанное количество подключений к базе данных для (int i = 0; i <this.size; i ++) {подключает [i] = new connect (); }} // Получить соединение базы данных // Вычитайте количество доступных подключений ...; System.out.println ("【"+thread.currentThread (). GetName ()+"】 Чтобы получить оставшееся количество оставшихся соединений:"+доступно); // Возврат ссылки на подключение return Connects [i]; }} return null; } // Получить соединение public connect openconce () бросает прерванную эксплуатацию {// получить лицензию semaphore.acquire (); // Получить подключение к базе данных return getConnect (); } // Выпуск подключения общедоступного синхронизированного выпуска void (Connect Connect) {for (int i = 0; i <this.size; i ++) {if (connect == connects [i]) {// Установите соединение, чтобы не использовать ConnectFlag [i] = false; // добавить 1 доступный номер подключения; System.out.println ("【"+Thread.currentThread (). GetName ()+"] для выпуска оставшегося номера подключения:"+доступен); // Выпуск лицензии semaphore.release (); }}} // Добавить количество доступных соединений public int доступен () {return доступен; }}Тестовый код:
Public Class TestThread Extends Thread {Private Static ConnectPool Pool = New ConnectPool (3); @Override public void run () {try {connect connect = pool.openconnect (); Thread.sleep (100); // взять бассейн перерывов. } catch (прерванное искусство e) {e.printstacktrace (); }} public static void main (string [] args) {for (int i = 0; i <10; i ++) {new TestThread (). start (); }}}Результаты теста:
Мы используем массив для хранения ссылок на подключения к базе данных. При инициализации пула соединений мы будем называть метод initConnects для создания указанного количества подключений к базе данных и сохранить их ссылки в массиве. Кроме того, существует массив того же размера, чтобы записать, доступно ли соединение. Всякий раз, когда внешний поток запрашивает подключение, сначала вызовите метод Semaphore.acquire (), чтобы получить лицензию, затем установите состояние подключения для использования и, наконец, возвращайте ссылку на соединение. Количество лицензий определяется параметрами, передаваемыми во время строительства. Количество лицензий уменьшается на 1 каждый раз, когда называется метод Semaphore.acquire (). Когда число сокращается до 0, это означает, что подключения нет. В настоящее время, если другие потоки получат его снова, они будут заблокированы. Всякий раз, когда поток выпускает соединение, Semaphore.release () будет вызван для выпуска лицензии. В настоящее время общее количество лицензий снова увеличится, что означает, что количество доступных соединений увеличилось. Затем ранее заблокированный поток проснется и продолжит получать соединение. В настоящее время вы можете успешно получить соединение, получив его снова. В примере испытания пул соединений из 3 соединений инициализируется. Из результатов теста мы видим, что всякий раз, когда поток получает соединение, оставшиеся количество подключений будет уменьшено на 1. Когда поток уменьшается до 0, другие потоки больше не могут его получить. В настоящее время вы должны подождать, пока поток выпустит соединение, прежде чем продолжить его получить. Вы можете видеть, что количество оставшихся соединений всегда изменяется от 0 до 3, что означает, что наш тест был успешным.
Выше всего содержание этой статьи. Я надеюсь, что это будет полезно для каждого обучения, и я надеюсь, что все будут поддерживать Wulin.com больше.