What is thread-safety and how to achieve it
Issue: BAEL-2416
This commit is contained in:
parent
65b9909fed
commit
04743892db
|
@ -0,0 +1,86 @@
|
|||
package com.baeldung.concurrent.threadsafety.application;
|
||||
|
||||
import com.baeldung.concurrent.threadsafety.callables.AtomicCounterCallable;
|
||||
import com.baeldung.concurrent.threadsafety.mathutils.MathUtils;
|
||||
import com.baeldung.concurrent.threadsafety.callables.CounterCallable;
|
||||
import com.baeldung.concurrent.threadsafety.callables.ExtrinsicLockCounterCallable;
|
||||
import com.baeldung.concurrent.threadsafety.callables.MessageServiceCallable;
|
||||
import com.baeldung.concurrent.threadsafety.callables.ReentranReadWriteLockCounterCallable;
|
||||
import com.baeldung.concurrent.threadsafety.callables.ReentrantLockCounterCallable;
|
||||
import com.baeldung.concurrent.threadsafety.services.AtomicCounter;
|
||||
import com.baeldung.concurrent.threadsafety.services.Counter;
|
||||
import com.baeldung.concurrent.threadsafety.services.ExtrinsicLockCounter;
|
||||
import com.baeldung.concurrent.threadsafety.services.MessageService;
|
||||
import com.baeldung.concurrent.threadsafety.services.ReentrantLockCounter;
|
||||
import com.baeldung.concurrent.threadsafety.services.ReentrantReadWriteLockCounter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) throws InterruptedException, ExecutionException {
|
||||
|
||||
new Thread(() -> {
|
||||
System.out.println(MathUtils.factorial(10));
|
||||
}).start();
|
||||
new Thread(() -> {
|
||||
System.out.println(MathUtils.factorial(5));
|
||||
}).start();
|
||||
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(10);
|
||||
MessageService messageService = new MessageService("Welcome to Baeldung!");
|
||||
Future<String> future1 = (Future<String>) executorService.submit(new MessageServiceCallable(messageService));
|
||||
Future<String> future2 = (Future<String>) executorService.submit(new MessageServiceCallable(messageService));
|
||||
System.out.println(future1.get());
|
||||
System.out.println(future2.get());
|
||||
|
||||
Counter counter = new Counter();
|
||||
Future<Integer> future3 = (Future<Integer>) executorService.submit(new CounterCallable(counter));
|
||||
Future<Integer> future4 = (Future<Integer>) executorService.submit(new CounterCallable(counter));
|
||||
System.out.println(future3.get());
|
||||
System.out.println(future4.get());
|
||||
|
||||
ExtrinsicLockCounter extrinsicLockCounter = new ExtrinsicLockCounter();
|
||||
Future<Integer> future5 = (Future<Integer>) executorService.submit(new ExtrinsicLockCounterCallable(extrinsicLockCounter));
|
||||
Future<Integer> future6 = (Future<Integer>) executorService.submit(new ExtrinsicLockCounterCallable(extrinsicLockCounter));
|
||||
System.out.println(future5.get());
|
||||
System.out.println(future6.get());
|
||||
|
||||
ReentrantLockCounter reentrantLockCounter = new ReentrantLockCounter();
|
||||
Future<Integer> future7 = (Future<Integer>) executorService.submit(new ReentrantLockCounterCallable(reentrantLockCounter));
|
||||
Future<Integer> future8 = (Future<Integer>) executorService.submit(new ReentrantLockCounterCallable(reentrantLockCounter));
|
||||
System.out.println(future7.get());
|
||||
System.out.println(future8.get());
|
||||
|
||||
ReentrantReadWriteLockCounter reentrantReadWriteLockCounter = new ReentrantReadWriteLockCounter();
|
||||
Future<Integer> future9 = (Future<Integer>) executorService.submit(new ReentranReadWriteLockCounterCallable(reentrantReadWriteLockCounter));
|
||||
Future<Integer> future10 = (Future<Integer>) executorService.submit(new ReentranReadWriteLockCounterCallable(reentrantReadWriteLockCounter));
|
||||
System.out.println(future9.get());
|
||||
System.out.println(future10.get());
|
||||
|
||||
AtomicCounter atomicCounter = new AtomicCounter();
|
||||
Future<Integer> future11 = (Future<Integer>) executorService.submit(new AtomicCounterCallable(atomicCounter));
|
||||
Future<Integer> future12 = (Future<Integer>) executorService.submit(new AtomicCounterCallable(atomicCounter));
|
||||
System.out.println(future11.get());
|
||||
System.out.println(future12.get());
|
||||
|
||||
Collection<Integer> syncCollection = Collections.synchronizedCollection(new ArrayList<>());
|
||||
Thread thread11 = new Thread(() -> syncCollection.addAll(Arrays.asList(1, 2, 3, 4, 5, 6)));
|
||||
Thread thread12 = new Thread(() -> syncCollection.addAll(Arrays.asList(1, 2, 3, 4, 5, 6)));
|
||||
thread11.start();
|
||||
thread12.start();
|
||||
|
||||
Map<String,String> concurrentMap = new ConcurrentHashMap<>();
|
||||
concurrentMap.put("1", "one");
|
||||
concurrentMap.put("2", "two");
|
||||
concurrentMap.put("3", "three");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.baeldung.concurrent.threadsafety.callables;
|
||||
|
||||
import com.baeldung.concurrent.threadsafety.services.AtomicCounter;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class AtomicCounterCallable implements Callable<Integer> {
|
||||
|
||||
private final AtomicCounter counter;
|
||||
|
||||
public AtomicCounterCallable(AtomicCounter counter) {
|
||||
this.counter = counter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer call() throws Exception {
|
||||
counter.incrementCounter();
|
||||
return counter.getCounter();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.baeldung.concurrent.threadsafety.callables;
|
||||
|
||||
import com.baeldung.concurrent.threadsafety.services.Counter;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class CounterCallable implements Callable<Integer> {
|
||||
|
||||
private final Counter counter;
|
||||
|
||||
public CounterCallable(Counter counter) {
|
||||
this.counter = counter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer call() throws Exception {
|
||||
counter.incrementCounter();
|
||||
return counter.getCounter();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.baeldung.concurrent.threadsafety.callables;
|
||||
|
||||
import com.baeldung.concurrent.threadsafety.services.ExtrinsicLockCounter;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class ExtrinsicLockCounterCallable implements Callable<Integer> {
|
||||
|
||||
private final ExtrinsicLockCounter counter;
|
||||
|
||||
public ExtrinsicLockCounterCallable(ExtrinsicLockCounter counter) {
|
||||
this.counter = counter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer call() throws Exception {
|
||||
counter.incrementCounter();
|
||||
return counter.getCounter();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.baeldung.concurrent.threadsafety.callables;
|
||||
|
||||
import com.baeldung.concurrent.threadsafety.services.MessageService;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class MessageServiceCallable implements Callable<String> {
|
||||
|
||||
private final MessageService messageService;
|
||||
|
||||
public MessageServiceCallable(MessageService messageService) {
|
||||
this.messageService = messageService;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String call() {
|
||||
return messageService.getMesssage();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.baeldung.concurrent.threadsafety.callables;
|
||||
|
||||
import com.baeldung.concurrent.threadsafety.services.ReentrantReadWriteLockCounter;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class ReentranReadWriteLockCounterCallable implements Callable<Integer> {
|
||||
|
||||
private final ReentrantReadWriteLockCounter counter;
|
||||
|
||||
public ReentranReadWriteLockCounterCallable(ReentrantReadWriteLockCounter counter) {
|
||||
this.counter = counter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer call() throws Exception {
|
||||
counter.incrementCounter();
|
||||
return counter.getCounter();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.baeldung.concurrent.threadsafety.callables;
|
||||
|
||||
import com.baeldung.concurrent.threadsafety.services.ReentrantLockCounter;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class ReentrantLockCounterCallable implements Callable<Integer> {
|
||||
|
||||
private final ReentrantLockCounter counter;
|
||||
|
||||
public ReentrantLockCounterCallable(ReentrantLockCounter counter) {
|
||||
this.counter = counter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer call() throws Exception {
|
||||
counter.incrementCounter();
|
||||
return counter.getCounter();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.baeldung.concurrent.threadsafety.mathutils;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class MathUtils {
|
||||
|
||||
public static BigInteger factorial(int number) {
|
||||
BigInteger f = new BigInteger("1");
|
||||
for (int i = 2; i <= number; i++) {
|
||||
f = f.multiply(BigInteger.valueOf(i));
|
||||
}
|
||||
return f;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package com.baeldung.concurrent.threadsafety.services;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class AtomicCounter {
|
||||
|
||||
private final AtomicInteger counter = new AtomicInteger();
|
||||
|
||||
public AtomicCounter() {}
|
||||
|
||||
public void incrementCounter() {
|
||||
counter.incrementAndGet();
|
||||
}
|
||||
|
||||
public synchronized int getCounter() {
|
||||
return counter.get();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package com.baeldung.concurrent.threadsafety.services;
|
||||
|
||||
public class Counter {
|
||||
|
||||
private volatile int counter;
|
||||
|
||||
public Counter() {
|
||||
this.counter = 0;
|
||||
}
|
||||
|
||||
public synchronized void incrementCounter() {
|
||||
counter += 1;
|
||||
}
|
||||
|
||||
public int getCounter() {
|
||||
return counter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package com.baeldung.concurrent.threadsafety.services;
|
||||
|
||||
public class ExtrinsicLockCounter {
|
||||
|
||||
private int counter;
|
||||
private final Object lock = new Object();
|
||||
|
||||
public ExtrinsicLockCounter() {
|
||||
this.counter = 0;
|
||||
}
|
||||
|
||||
public void incrementCounter() {
|
||||
synchronized (lock) {
|
||||
counter += 1;
|
||||
}
|
||||
}
|
||||
|
||||
public int getCounter() {
|
||||
synchronized (lock) {
|
||||
return counter;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.baeldung.concurrent.threadsafety.services;
|
||||
|
||||
public class MessageService {
|
||||
|
||||
private final String message;
|
||||
|
||||
public MessageService(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMesssage() {
|
||||
return message;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.baeldung.concurrent.threadsafety.services;
|
||||
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class ReentrantLockCounter {
|
||||
|
||||
private int counter;
|
||||
private final ReentrantLock reLock = new ReentrantLock(true);
|
||||
|
||||
public ReentrantLockCounter() {
|
||||
this.counter = 0;
|
||||
}
|
||||
|
||||
public void incrementCounter() {
|
||||
reLock.lock();
|
||||
try {
|
||||
counter += 1;
|
||||
} finally {
|
||||
reLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public int getCounter() {
|
||||
return counter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package com.baeldung.concurrent.threadsafety.services;
|
||||
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
public class ReentrantReadWriteLockCounter {
|
||||
|
||||
private int counter;
|
||||
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
|
||||
private final Lock readLock = rwLock.readLock();
|
||||
private final Lock writeLock = rwLock.writeLock();
|
||||
|
||||
public ReentrantReadWriteLockCounter() {
|
||||
this.counter = 0;
|
||||
}
|
||||
|
||||
public void incrementCounter() {
|
||||
writeLock.lock();
|
||||
try {
|
||||
counter += 1;
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public int getCounter() {
|
||||
readLock.lock();
|
||||
try {
|
||||
return counter;
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.baeldung.concurrent.threadsafety.services;
|
||||
|
||||
public class StateHolder {
|
||||
|
||||
private final String state;
|
||||
|
||||
public StateHolder(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package com.baeldung.concurrent.threadsafety.tests;
|
||||
|
||||
import org.junit.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import com.baeldung.concurrent.threadsafety.callables.CounterCallable;
|
||||
import com.baeldung.concurrent.threadsafety.services.Counter;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class CounterTest {
|
||||
|
||||
@Test
|
||||
public void whenCalledIncrementCounter_thenCorrect() throws Exception {
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(2);
|
||||
Counter counter = new Counter();
|
||||
Future<Integer> future1 = (Future<Integer>) executorService.submit(new CounterCallable(counter));
|
||||
Future<Integer> future2 = (Future<Integer>) executorService.submit(new CounterCallable(counter));
|
||||
|
||||
assertThat(future1.get()).isEqualTo(1);
|
||||
assertThat(future2.get()).isEqualTo(2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package com.baeldung.concurrent.threadsafety.tests;
|
||||
|
||||
import org.junit.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import com.baeldung.concurrent.threadsafety.callables.ExtrinsicLockCounterCallable;
|
||||
import com.baeldung.concurrent.threadsafety.services.ExtrinsicLockCounter;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class ExtrinsicLockCounterTest {
|
||||
|
||||
@Test
|
||||
public void whenCalledIncrementCounter_thenCorrect() throws Exception {
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(2);
|
||||
ExtrinsicLockCounter counter = new ExtrinsicLockCounter();
|
||||
Future<Integer> future1 = (Future<Integer>) executorService.submit(new ExtrinsicLockCounterCallable(counter));
|
||||
Future<Integer> future2 = (Future<Integer>) executorService.submit(new ExtrinsicLockCounterCallable(counter));
|
||||
|
||||
assertThat(future1.get()).isEqualTo(1);
|
||||
assertThat(future2.get()).isEqualTo(2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.baeldung.concurrent.threadsafety.tests;
|
||||
|
||||
import com.baeldung.concurrent.threadsafety.mathutils.MathUtils;
|
||||
import org.junit.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class MathUtilsTest {
|
||||
|
||||
@Test
|
||||
public void whenCalledFactorialMethod_thenCorrect() {
|
||||
assertThat(MathUtils.factorial(2)).isEqualTo(2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package com.baeldung.concurrent.threadsafety.tests;
|
||||
|
||||
import org.junit.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import com.baeldung.concurrent.threadsafety.callables.MessageServiceCallable;
|
||||
import com.baeldung.concurrent.threadsafety.services.MessageService;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class MessageServiceTest {
|
||||
|
||||
@Test
|
||||
public void whenCalledgetMessage_thenCorrect() throws Exception {
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(2);
|
||||
MessageService messageService = new MessageService("Welcome to Baeldung!");
|
||||
Future<String> future1 = (Future<String>) executorService.submit(new MessageServiceCallable(messageService));
|
||||
Future<String> future2 = (Future<String>) executorService.submit(new MessageServiceCallable(messageService));
|
||||
|
||||
assertThat(future1.get()).isEqualTo("Welcome to Baeldung!");
|
||||
assertThat(future2.get()).isEqualTo("Welcome to Baeldung!");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package com.baeldung.concurrent.threadsafety.tests;
|
||||
|
||||
import com.baeldung.concurrent.threadsafety.callables.ReentrantLockCounterCallable;
|
||||
import com.baeldung.concurrent.threadsafety.services.ReentrantLockCounter;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ReentrantLockCounterTest {
|
||||
|
||||
@Test
|
||||
public void whenCalledIncrementCounter_thenCorrect() throws Exception {
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(2);
|
||||
ReentrantLockCounter counter = new ReentrantLockCounter();
|
||||
Future<Integer> future1 = (Future<Integer>) executorService.submit(new ReentrantLockCounterCallable(counter));
|
||||
Future<Integer> future2 = (Future<Integer>) executorService.submit(new ReentrantLockCounterCallable(counter));
|
||||
|
||||
assertThat(future1.get()).isEqualTo(1);
|
||||
assertThat(future2.get()).isEqualTo(2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.baeldung.concurrent.threadsafety.tests;
|
||||
|
||||
import com.baeldung.concurrent.threadsafety.callables.ReentranReadWriteLockCounterCallable;
|
||||
import com.baeldung.concurrent.threadsafety.services.ReentrantReadWriteLockCounter;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ReentrantReadWriteLockCounterTest {
|
||||
|
||||
@Test
|
||||
public void whenCalledIncrementCounter_thenCorrect() throws Exception {
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(2);
|
||||
ReentrantReadWriteLockCounter counter = new ReentrantReadWriteLockCounter();
|
||||
Future<Integer> future1 = (Future<Integer>) executorService.submit(new ReentranReadWriteLockCounterCallable(counter));
|
||||
Future<Integer> future2 = (Future<Integer>) executorService.submit(new ReentranReadWriteLockCounterCallable(counter));
|
||||
|
||||
assertThat(future1.get()).isEqualTo(1);
|
||||
assertThat(future2.get()).isEqualTo(2);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue