What is thread safety

  • Giving an exact definition of thread safety is very complex. The more formal the definition, the more complex it is, not only difficult to provide meaningful guidance, but also difficult to understand intuitively. So here are some informal descriptions that seem guilty. Many “definitions” can be searched on the Internet, for example
  1. Can be called in multiple threads, and there is no errant interaction between threads.
  2. Can be called by multiple threads at the same time without requiring the caller to perform additional actions.
  • Looking at these definitions, it’s no wonder we’re confused about thread safety. They sound a lot like “if a class can be used safely in multiple threads, it is a thread-safe class.” There’s not much debate about this statement, but it’s also not very helpful. How do we distinguish between thread-safe classes and non-thread-safe classes? Further, what does “safe” mean?

  • In the definition of thread safety, the most central concept is correctness. If the definition of thread-safety is vague, it is due to the lack of a clear definition of correctness.

  • Correctness means that a class behaves exactly as its specification does. In a good specification it is common to define various invariance conditions to constrain the state of an object and a variety of posterior conditions to describe the results of an object operation.

  • With a clearer definition of correctness, thread-safety can be defined:

A class is thread-safe if it behaves correctly when accessed by multiple threads, regardless of the scheduling method used by the runtime environment or how the threads will execute alternately, and requires no additional synchronization or coordination of tasks in the calling code.

atomic

Refers to the indivisibility of a transaction in which all or none of the operations of a transaction are executed without interruption.

Race conditions:

A race condition occurs when the correctness of the calculation depends on the execution of multiple threads alternately.

Race conditions occur when the correctness of a calculation depends on the alternate execution timing of multiple threads. In other words, the right result is a matter of luck. The most common type of race condition is the “check before you execute” operation, where a failed observation is used to determine the next action.

  • Examples of race conditions

(CAS: compare and swap- compare and swap)

  • When multiple threads perform CAS operations on a resource at the same time, only one thread succeeds in the operation, but the other threads do not block, and the other threads receive a failure signal. So CAS is actually an optimistic lock.
  • To avoid the problem of race conditions, you must somehow prevent other threads from using the variable while one thread is modifying it, ensuring that other threads can only read and modify the state before or after the modification operation is complete, not while it is being modified.

Locking mechanism

Built-in lock:

Java provides a built-in locking mechanism to support atomicity: Synchronized blocks. A synchronized code block consists of two parts: an object reference as a lock and a code block as a code block protected by the lock. A method modified with the keyword synchronized is a synchronized code block that spans the entire method body, where the lock of the synchronized code block is the object on which the method call is made. Static synchronized methods use Class objects as locks.

Java’s built-in lock acts as a mutex (or mutex), which means that at most one thread can hold the lock. When thread A attempts to acquire A lock held by thread B, thread A must wait or block until thread B releases the lock. If B never releases the lock, A will wait forever.