AtomicInteger принадлежит к семейству атомных переменных. Основным преимуществом является то, что его использование не блокирует, а выполняет блокирующую синхронизацию, таким образом вы избегаете приостановки и перепланирования потока.
AtomicInteger основан на механизме сравнения и обмена и является частью скалярной группы атомарных переменных.
Наш первый вариант использования — это функция на веб-странице, к которой можно обращаться несколько раз.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
package com.gkatzioura.concurrency;import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerExample { private AtomicInteger atomicInteger = new AtomicInteger(); public void serveRequest() { atomicInteger.incrementAndGet(); /** * logic */ } public int requestsServed() { return atomicInteger.get(); }} |
И тест для нашего варианта использования
|
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
|
package com.gkatzioura.concurrency;import java.util.ArrayList;import java.util.List;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;import org.junit.jupiter.api.Assertions;import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.Test;public class AtomicIntegerExampleTest { private AtomicIntegerExample atomicIntegerExample; @BeforeEach void setUp() { atomicIntegerExample = new AtomicIntegerExample(); } @Test void testConcurrentIncrementAndGet() throws ExecutionException, InterruptedException { final int threads = 10; ExecutorService executorService = Executors.newFixedThreadPool(threads); List<Future> futures = new ArrayList(); for (int i = 0; i { atomicIntegerExample.serveRequest(); return null; })); } for(Future future: futures) { future.get(); } Assertions.assertEquals(10,atomicIntegerExample.requestsServed()); }} |
Помимо использования атомарного целого числа в качестве счетчика, вы можете использовать его в различных случаях. Например потокобезопасный алгоритм циклического перебора.
|
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
|
package com.gkatzioura.concurrency;import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerRoundRobin { private final int totalIndexes; private final AtomicInteger atomicInteger = new AtomicInteger(-1); public AtomicIntegerRoundRobin(int totalIndexes) { this.totalIndexes = totalIndexes; } public int index() { int currentIndex; int nextIndex; do { currentIndex = atomicInteger.get(); nextIndex = currentIndex< Integer.MAX_VALUE ? currentIndex+1: 0; } while (!atomicInteger.compareAndSet(currentIndex, nextIndex)); return nextIndex % totalIndexes; }} |
TotalIndex — это общее количество индексов. Когда запрашивается запрос следующего индекса, счетчик должен быть увеличен, и будет выполнена операция сравнения и задания. Если произойдет сбой из-за другого потока, он снова попытается выполнить операцию и получит следующее значение счетчика.
Операция по модулю даст текущий индекс. Если атомное целое число достигает максимального значения, оно должно быть сброшено до нуля. Сброс может вызвать крайний случай и изменить порядок индексов. Если это проблема, вы можете настроить максимальное значение на основе общего размера индекса, чтобы избежать этого.
Также некоторые испытания по этому вопросу.
|
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
|
package com.gkatzioura.concurrency;import java.util.ArrayList;import java.util.List;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;import org.junit.jupiter.api.Assertions;import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.Test;class AtomicIntegerRoundRobinTest { private static final int MAX_INDEX = 10; private AtomicIntegerRoundRobin atomicIntegerRoundRobin; @BeforeEach void setUp() { atomicIntegerRoundRobin = new AtomicIntegerRoundRobin(MAX_INDEX); } @Test void testIndexesSerially() { for(long i=0;i<MAX_INDEX*20;i++) { System.out.println(atomicIntegerRoundRobin.index()); } Assertions.assertEquals(0, atomicIntegerRoundRobin.index()); } @Test void testIndexesConcurrently() throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(4); List<Future> futures = new ArrayList(); for (int i = 0; i atomicIntegerRoundRobin.index())); } for(Future future: futures) { System.out.println(future.get()); } Assertions.assertEquals(0,atomicIntegerRoundRobin.index()); }} |
|
Опубликовано на Java Code Geeks с разрешения Эммануила Гкациоураса, партнера нашей программы JCG. Смотрите оригинальную статью здесь: AtomicInteger на Java и Round-Robin Мнения, высказанные участниками Java Code Geeks, являются их собственными. |