Εμφάνιση αναρτήσεων με ετικέτα runnable. Εμφάνιση όλων των αναρτήσεων
Εμφάνιση αναρτήσεων με ετικέτα runnable. Εμφάνιση όλων των αναρτήσεων

Παρασκευή 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  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