What is thread-safety and how to achieve it

Issue: BAEL-2416
This commit is contained in:
Alejandro Gervasio 2019-01-07 08:17:52 -03:00 committed by Josh Cummings
parent 65b9909fed
commit 04743892db
21 changed files with 491 additions and 0 deletions

View File

@ -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");
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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!");
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}