Concurrency — in a Multi-Threaded Environment (Thread Concepts and Locks)

Hanwen Zhang
3 min readJun 22, 2022

--

Photo by Joseph Greve on Unsplash
  • Thread is the smallest unit of execution scheduled by OS.
  • A task is a work that is performed by a thread.
  • A process is a set of threads that execute in the same shared environment.
  • Executing multiple threads or processes at a time is called concurrency.
  • Multi-thread programming is doing task multiple tasks in parallel processing — running multiple processors (CPUs) to handle separate parts of a task.
  • Java is a Multi-threaded language while JavaScript is single-threaded.

Why Multi-thread Feature?

Performing an operation inside memory is very fast, but accessing the disk and performing I/O is a slow process. While waiting for information gets available from the disk can be slow which leads to poor performance.

Thread Scheduler

Operating systems use a thread scheduler to determine which thread should be running on a CPU core.

Java enables us to assign a priority to a thread.

  • Thread Priority setPriority(int), Thread.MIN_PRIORITY, Thread.NORM_PRIORITY, Thread.MAX_PRIORITY

Run Inside a Thread

java.lang.Runnable is a functional java interface (only one abstract method) that is used to define the work that a thread should execute.

  • Thread is a class
  • Runnable is an interface
  • Thread class implements Runnable
public class ThreadExample implements Runnable {
public void run(){
// what is running inside the thread goes here
}}
@FunctionalInterface
public interface Runnable {
void run();
}

Create a Thread (NO guarantee — the order of threads to be executed in Java)

  1. Defining the thread within the tasks it should perform.
  2. Starting the thread.

Race Condition Problem: multiple threads access shared variable. The value of the variable depends on the execution order of threads.

Two Scenarios for Race Condition

  • Read-modify-write (multiple threads read a given variable, then modify its value and write it back, the new state is derived from the previous state, an example iscounter++)
  • Check-then-act (multiple threads check a given condition, the example is to check for key existence in Map, which additional locking is required, and/or thread synchronization in critical sections)

Data Race: when multiple threads access the shared variable in the same memory location concurrently and operations are not synchronized, in which at least one thread is writing.

How to Avoid Race Condition & Data Race

we need to make sure resources that can be shared between threads — must be executed atomically.

How to Achieve Atomic Operations

Atomic Operations—ensures no race condition, any operation on a shared resource ensures the order of access and guarantees correct updates.

  • Locking Mechanism
  • Synchronized method — called mutually exclusive
  • Atomic Class (AtomicInteger, AtomicBoolean)
  • volatile — ensure the visibility of variablesprivate static volatile int volatileCounter
  • Thread Priority setPriority(int), Thread.MIN_PRIORITY, Thread.NORM_PRIORITY, Thread.MAX_PRIORITY
  • Thread Forking — duplicates the current process as a brand new one
  • Database Sequence — generating unique values to prevent concurrency
  • Dekker’s Algorithm (toggle turns on -> enter critical section (processes access shared resources and perform operations)-> turns the toggle off)

Locks

  • Structured Lock—we can use the synchronized keyword imposes a lock

disadvantage: 1. disable the multitasking feature, 2. protects data integrity but the response time, 3. performance bottleneck — cannot meet specified system requirements, cannot perform the tasks demanded of it.

  • Unstructured Locks — locks are nested inside each other

Solution: 1.ReentrantLock — hold the lock until the thread is done. 2. Read/Write Lock — implement separate read and write locks (More than one thread can hold a Read Lock, but only one thread can hold the Write Lock).

Semantic Errors in Concurrency

  • DeadLock — locking each other
  • LiveLock — in a mode do not make any progress toward finishing their task
  • Starvation — too many small threads, some might never get called

Solution: Executor — an interface, execute() takes a Runnable object and completes the task, submit() returns the future object, the result of thread execution.

Photo by Emile Perron on Unsplash

--

--

Hanwen Zhang

Full-Stack Software Engineer at a Healthcare Tech Company | Document My Coding Journey | Improve My Knowledge | Share Coding Concepts in a Simple Way