Παρασκευή 20 Ιανουαρίου 2023

Concurrency with Synchronized keywork and Atomic classes


 
ACHIEVING SYNCHRONIZATION
 FOR A SHARED INSTANCE BETWEEN MULTIPLE THREADS



Lets suppose we have many threads operating simultaneously on an SynchronizationWith instance which increments a counter by 1 

But we don't want 2 or more to operate at the same time

Because this will create inconsistencies on our controlled adding - counter may increase faster and lose a value. 

So, only one thread should have access to read and write at each moment.

The next thread should get the last value and increment/write on it.

And so on.



THREADS AND MAIN MEMORY








WITH SYNCHRONIZED KEYWORD



Class/Instance for incrementing counter

public class SynchronizationWithSynchronizedKeyword {
private int counter;

int getValue() {
return counter;
}

synchronized void incrementBy1() {
counter++;
}
}

- Synchronized keyword will:

    - Stop other threads from accessing (locking)
    - Read the last value from the main memory
    - Increment it by 1
    - Write the new value in shared memory




Testing

@Test
public void test1() throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(3);
SynchronizationWithSynchronizedKeyword syncWithKeyword = new SynchronizationWithSynchronizedKeyword();

IntStream.range(0, 1000).forEach(count -> service.execute(syncWithKeyword::incrementBy1));
shutdownAndAwaitTermination(service);

assertEquals(1000, syncWithKeyword.getValue());
}
- Shared instance: syncWithKeyword
- We initiate 1000 Runnable tasks via a 3-Thread pool
- These tasks reference the shared instance's increment method
- To pass test, each task should increase the counter variable by 1, adding totally to 1000.



Helper method to gracefully shutdown the executor threads
private void shutdownAndAwaitTermination(ExecutorService pool) {
// Disable new tasks from being submitted
pool.shutdown();
try {
// Wait a while for existing tasks to terminate
if (!pool.awaitTermination(100, TimeUnit.MILLISECONDS)) {
// Cancel currently executing tasks forcefully
pool.shutdownNow();
// Wait a while for tasks to respond to being cancelled
if (!pool.awaitTermination(100, TimeUnit.MILLISECONDS))
System.err.println("Pool did not terminate");
}
} catch (InterruptedException ex) {
// (Re-)Cancel if current thread also interrupted
pool.shutdownNow();
// Preserve interrupt status
Thread.currentThread().interrupt();
}
}
Result: Success





WITH ATOMIC VARIABLE CLASSES



Class/Instance for incrementing counter
public class SynchronizationWithAtomicClasses {
private final AtomicInteger counter = new AtomicInteger(0);

int getValue() {
return counter.get();
}

void incrementBy1() {
counter.incrementAndGet();
}
}
- AtomicInteger's incrementAndGet when called from a thread, will (atomic operation) :

    - Stop other threads from accessing (locking)
    - Read the last value from the main memory
    - Increment it by 1
    - Write the new value in shared memory



Testing

(Same as above)
@Test
public void test2() throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(3);
SynchronizationWithAtomicClasses syncWithAtomic = new SynchronizationWithAtomicClasses();

IntStream.range(0, 1000).forEach(count -> service.execute(syncWithAtomic::incrementBy1));
shutdownAndAwaitTermination(service);

assertEquals(1000, syncWithAtomic.getValue());
}
Result: Success



Δεν υπάρχουν σχόλια:

Δημοσίευση σχολίου

What may be missing, or could get better?