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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user