Язык Java предоставляет множество способов обработки условий гонки при работе с параллельными потоками, обращающимися к общему ресурсу. Некоторые включают;
- Используя ключевое слово volatile
 - Использование классов, доступных в java.util.concurrent и java.util.concurrent.atomic
 - Синхронизированные блоки
 - Используя семафор
 
  Конечно, я мог бы не знать о многих других вещах.  На сегодня я хочу показать вам пример, использующий 
  Семафор  Это было введено в JDK 1.5 и дает разработчику возможность беспрепятственно получать и снимать блокировки.  Также пример, который я покажу, является гипотетическим сценарием, который я использовал только для того, чтобы показать, чего можно достичь с помощью семафора, и поэтому, пожалуйста, не смотрите на внутренние детали кода 🙂 .. 
  Таким образом, сценарий как таковой, есть кэш в памяти, содержащий объекты типа 
  ‘Человек’.  Пользователи могут вставлять и извлекать записи, используя кэш.  Проблема в том, что мы собираемся контролировать одновременный доступ к нашему кэшу в памяти с помощью семафоров.  Теперь я не хочу утомлять вас большим количеством текста, поэтому давайте приступим к делу и покажем немного кода; 
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 
64 
65 
 | 
import java.util.concurrent.Semaphore;/** * This class will allow thread to acquire and release locks as required *  * @author dinuka.arseculeratne *  */public class PersonLock { /**  * We do not want multiple lock objects lying around so we make ths class  * singleton  */ private PersonLock() { } /**  * Bill Pugh's way of lazy initializing the singleton instance  *   * @author dinuka.arseculeratne  *   */ private static class SingletonHolder {  public static final PersonLock INSTANCE = new PersonLock(); } /**  * Use this method to get a reference to the singleton instance of  * {@link PersonLock}  *   * @return the singleton instance  */ public static PersonLock getInstance() {  return SingletonHolder.INSTANCE; } /**  * In this sample, we allow only one thread at at time to update the cache  * in order to maintain consistency  */ private Semaphore writeLock = new Semaphore(1); /**  * We allow 10 concurrent threads to access the cache at any given time  */ private Semaphore readLock = new Semaphore(10); public void getWriteLock() throws InterruptedException {  writeLock.acquire(); } public void releaseWriteLock() {  writeLock.release(); } public void getReadLock() throws InterruptedException {  readLock.acquire(); } public void releaseReadLock() {  readLock.release(); }} | 
Этот класс будет обрабатывать процесс получения и освобождения блокировок, необходимых для обеспечения безопасности нашего потока кеша. Я использовал два отдельных замка для чтения и записи. Основанием для этого было позволить пользователям читать данные, хотя они могут быть устаревшими во время чтения.
  Обратите внимание, что я использовал 
  «десять» здесь означает, что десять потоков могут одновременно получать блокировки и обращаться к кешу для чтения.  Далее вы можете увидеть в блокировке записи, я использовал 
  один ‘, который означает, что только один поток может одновременно обращаться к кешу для помещения в него элементов.  Это важно для обеспечения согласованности в кэше.  То есть я не хочу, чтобы несколько потоков пытались вставить элементы на карту, что привело бы к непредсказуемому поведению (по крайней мере, в некоторых случаях).  Есть в основном два способа получения блокировки с помощью семафора. 
1. acqu () : блокирующий вызов, который ожидает, пока блокировка не будет снята или поток не прерван
2. tryAcquire () : это неблокирующий вызов, который немедленно вернется и вернет истину или ложь, показывая, была ли получена блокировка или нет.
Здесь я использовал блокирующий вызов захвата, потому что я хочу, чтобы поток ожидал, пока блокировка не станет доступной. Конечно, это будет зависеть от вашего варианта использования. Вы также можете определить период ожидания в методе tryAcquire (), чтобы поток не ожидал блокировки бесконечно.
Далее класс хранения ниже показывает, как я использовал класс блокировки для вставки и чтения данных в кеше.
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 
64 
65 
66 
67 
68 
69 
70 
71 
72 
73 
74 
75 
76 
77 
78 
79 
80 
81 
 | 
import java.util.HashMap;import java.util.Map;/** * A mock storage to hold the person objects in a map *  * @author dinuka.arseculeratne *  */public class PersonStorage { private Map<Integer, Person> personCache = new HashMap<Integer, Person>(); private int counter = 0; /**  * This class is made singleton and hence the constructor is made private  */ private PersonStorage() { } /**  * Bill Pugh's way of lazy initializing the singleton instance  *   * @author dinuka.arseculeratne  *   */ private static final class SingletonHolder {  public static final PersonStorage INSTANCE = new PersonStorage(); }   /**  * Use this method to get a reference to the singleton instance of  * {@link PersonStorage}  *   * @return the singleton instance  */ public static PersonStorage getInstance() {  return SingletonHolder.INSTANCE; } /**  * Inserts the person into the map. Note that we use defensive copying so  * that even if the client changes the object later on, those changes will  * not be reflected in the object within the map  *   * @param person  *            the instance of {@link Person} to be inserted  * @return the key which signifies the location of the person object  * @throws InterruptedException  */ public int putPerson(Person person) throws InterruptedException {     Person copyPerson = person.copyPerson();  personCache.put(++counter, copyPerson);     return counter; } /**  * Here as well we use defensive copying so that the value of the object  * reference within the map is not passed in to the calling party.  *   * @param id  *            the id representing the location of the object within the map  * @return the instance of the {@link Person} represented by the key passed  *         in  * @throws InterruptedException  */ public Person retrievePerson(int id) throws InterruptedException {  PersonLock.getInstance().getReadLock();  if (!personCache.containsKey(id)) {   throw new RuntimeException('Key is not found');  }  PersonLock.getInstance().releaseReadLock();  return personCache.get(id).copyPerson(); }} | 
Очевидно, что код будет работать и без блокировок, но проблема в том, что приложение будет несовместимым и будет давать разные результаты при каждом запуске. Это не то, что вы хотите, чтобы ваше приложение делало, и поэтому с блокировками вы гарантируете, что ваше приложение работает стабильно.
И, наконец, небольшой тестовый класс, чтобы показать, как он будет себя вести; не то чтобы здесь мы получили блокировку перед вызовом метода putPerson () и снимаем блокировку в блоке finally, чтобы гарантировать снятие блокировки.
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
 | 
/** * A test class to demonstrate the locking at work *  * @author dinuka.arseculeratne *  */public class TestLock { public static void main(String[] args) throws InterruptedException {  Thread t1 = new Thread(new Runnable() {   @Override   public void run() {         Person p1 = new Person(1L, 'Test1', 'XYZ');    try {PersonLock.getInstance().getWriteLock();PersonStorage.getInstance().putPerson(p1);    } catch (InterruptedException e) {     // Exception handling need to be done     e.printStackTrace();    }   finally{          PersonLock.getInstance().releaseWriteLock();    }   }  });  Thread t2 = new Thread(new Runnable() {   @Override   public void run() {         Person p2 = new Person(2L, 'Test123', 'ABC');    try {PersonLock.getInstance().getWriteLock();     PersonStorage.getInstance().putPerson(p2);    } catch (InterruptedException e) {     // Exception handling need to be done    } finally{          PersonLock.getInstance().releaseWriteLock();    }        }  });  t1.start();  t2.start();  System.out.println(PersonStorage.getInstance().retrievePerson(2)); }} | 
  На этом я заканчиваю свое краткое введение в использование Sempahores для обеспечения безопасности вашего кода. Для тех, кто хочет поиграть с кодом, вы можете получить его у 
  здесь  Попробуйте снять блокировки в классе Storage и посмотрите, как он ведет себя при каждом запуске.  Вы увидите возможные условия гонки. 
Ссылка: Блокировка с помощью семафора: пример от нашего партнера по JCG Динуки Арурилератне в блоге My Journey By IT .