Friday, 14 April 2023

Concurrent Programming Review

Process and Thread


The process is self-contained. It doesn't share the resource with others. A thread is a minimal execution unit, living inside a process. One process may consist of many threads. Threads may share a common resource with each other.

Volatile: Solving the visibility issue between threads.

Each Thread has its own local cache, so the variable defined in a thread won't be visible by another thread. When using Volatile keyword to modify a variable, the OS will push the variable into the shared cache(among the threads), thus it becomes visible to all other threads.

Volatile provides visibilities between isolated threads, however it doesn't provide an atomic protection from multiple step operation(read-modification-write).

Concurrent and Parallel computing 

Concurrent computing task can be decomposed into sub-tasks, which are order-independent.  These sub-tasks are carried out at the same core but different threads. Form an external view, it seems that these tasks are carried out simultaneously, but actually not.

Parallel computing needs parallel computing hardware first, i.e. having more than one core. The computing task is carried out in independent cores simultaneously.  

Mutex

Mutex means mutual exclusion is the way to solve the data race. At any time, only one thread is allowed to access the shared data. 

Data race: 

Simultaneously multiple threads access a common memory location.  The data stored there may be not consistent and being polluted. Ideally,  all steps should be carried out in a single uninterruptible operation, so as to prevent data from pollution. 

Lock: 

Two types of locks, i.e. extrinsic or intrinsic lock.

An extrinsic lock is explicitly declared from Lock interface and its implementations. A critical section is protected by an explicit lock and afterwards, the lock is unlocked.

Synchronization: 

Two types of synchronization, i.e. synchronization method or synchronization statements.

Every Java class or object has an intrinsic lock associated with it. The intrinsic lock must be achieved first, so as to access the synchronized method or statements.

Note: ➀watching out Synchronizing on the class or instance; ➁ don't synchronize on an immutable object, for it generates a new instance after its value being modified.

Atomic variables:

The most commonly used atomic variable classes in Java are AtomicInteger, AtomicLong, AtomicBoolean, and AtomicReference. These classes represent an int, long, boolean and object reference respectively which can be atomically updated.

For instance, AtomicInteger provides a method incrementAndGet(), it is not synchronized, but it offers
atomicity guarantees by using Low-level compare-and-set(CAS) instruction. 

Try Lock:

The tryLock method offers a non-blocking way to acquire the lock(explicit lock), so as to improve performance.

When a thread wants to acquire the lock, but not available, it may go away to carry out some other tasks, rather than being blocked and waiting there. This is the purpose to have 'try lock' method.

Read-write lock:

Instead of locking the resource to both read and write operations equally. A read-write lock is distinguished into a two-part lock, i.e. a read-alone lock and a write-alone lock. By this way, it may improve the performance. By this way, it allows multiple threads to read the shared resources simultaneously, for reading doesn't cause data race. It allows a single thread to write on the shared resource at a moment. This type of lock may be used when the number of reading threads is much bigger than the number of writing thread.

Note: read-write lock may use more resource under the hood.

Liveness


Deadlock:

Resource A and its Lock A; Resource B and its Lock B. Thread A hold Lock A, while Thread b hold Lock B.  Thread A acquires Lock B, while thread B acquires Lock A. 

How to prevent threads from deadlock?
lock priority: multiple threads acquire locks in a fixed order.
lock timeout: if cannot acquire, then quit.

Abandoned lock: 

when one thread acquired the lock, but it fails to unlock it due to exceptions. The lock is therefore abandoned. This may lead to the rest of the threads in stuck.

How to prevent a lock becoming abandoned?

using try-catch block to surround the critical section, and in the 'finally' block unlock the lock.

Starvation:

Starvation happens when a thread is perpetually denied resources due to competition with others. A thread may in starvation because it has a low priority. Or it simply because there are too many concurrent threads.

Livelock: 

Multiple threads are designed to respond to each other so as to solve the conflict.

To solve the livelock, making sure only one thread takes action. It may take use of random selection.




No comments:

Can Jackson Deserialize Java Time ZonedDateTime

Yes, but must include JSR310. Thus ZonedDateTime can be deserialized directly from JSON response to POJO field. <dependency> <g...