Introduce a,

1.1 How to Write thread-safe Code

  • The core is to manage state access operations.
  • Especially forShared (Shared)andVariable (Mutable)State access.

1.2 Shared and variable Meanings

  • Shared: Means that multiple threads can access it simultaneously
  • Mutable: Means that the value of a variable can change over its lifetime.

1.3 Do objects need to be thread-safe?

  • Depending on whether it is accessed by multiple threads.
  • Here refers toHow objects are accessed in a program, rather than what the object is supposed to do.
  • If you want to make objects thread-safe, you need to use synchronization,Collaborative thread access to the mutable state of an object.

1.4 How to cooperate with threads to access mutable state of objects?

  1. The keywordsynchronized
  2. volatileType variable
  3. Explicit Lock (explicit lock)
  4. Atomic variables (JUC package) : Implements atomic state transitions on numeric and object references.
    • AtomicBoolean
    • AtomicLong
    • AtomicInteger
    • AtomicReference

1.5 How can I fix the problem that multiple threads access the same mutable state variable?

Prerequisite: Assume that this problem is resolved due to the failure to use proper synchronization:

  1. Not shared between threadsThe state variable.
  2. Change the state variable toImmutable variable
  3. Used when accessing state variablessynchronous.

1.6 How to design a thread-safe class?

  1. Good object-oriented skills
  2. unmodifiability
  3. Explicit invariance specifications

What is thread safety

2.1 define

  • When multiple threads access a class, it is always availableShow the right behaviorSo let’s call it thisClasses are thread-safe
  • This class behaves correctly regardless of how the runtime environment is scheduled or how the threads will execute alternately, and without any additional synchronization or coordination in the calling code.

2.2 Definition of correctness

  • A class behaves exactly as its specification does.
  • In a good specification it is common to define variousThe Invarient conditions constrain the state of an object, and define variousA posterior condition is used to describe the result of an object operation.

2.3 Stateless objects must be thread-safe.

Atomicity

3.1 Race Condition

  1. define
    • Due to theIncorrect results resulting from improper execution of timing
  2. Nature:
    • Making a judgment or performing a calculation based on an observation that might not work can cause problems.
  3. Common race conditions
    • Check before you perform: Determine the next action based on an observation that may fail.
    • Read-modify-write: Defines a transition of an object’s state based on its previous state.

3.2 Compound Operation

  • Both of the above race conditions involve compound operations.
  • Contains a set of mandatoryAtomic wayOperations performed to ensure thread-safety.

3.3 Atomic Operations

  • This operation is an atomic operation for all operations that access the same state, including the operation itself.

Four, locking mechanism

4.1 the built-in lock

  1. Synchronized code block (Synchronized BlockJava provides a built-in locking mechanism to support atomicity.
  2. The composition of the synchronous code block: two parts
    • The object reference to the lock
    • Block of code protected by a lock
  3. Syncing block syntax format:
    synchronized (lock) {
      // Access/modify the lock protected share state
    }
Copy the code
  1. static synchronizedMethods toClass objectAs a lock.
  2. Intrinsic LockorMonitor Lock
    • Each Java object can be used as a lock to implement synchronization, and these locks are called built-in or monitor locks.
    • The thread automatically acquires the lock before entering the synchronized block and releases the lock after exiting the synchronized block.
    • Only one thread can execute a block of code protected by the built-in lock at a time.
  3. Built-in lock isReentrant lock.
    • Reentrant meansThe granularity of the lock acquisition operation is threadInstead of calling.

5. Use locks to protect status

  1. Pay attention to the point
    • Mutable state variables that may be accessed by multiple threads at the same time need to hold the same lock when accessing them. In this case we say that the state variable is protected by the lock.

Sixth, the demo

6.1 Requirements

  1. Premise: Suppose we want to get the number of requests from the user and increment it.
  2. requirements: Consider multithreaded execution so thatmainThe method outputs the correct result.
  3. The basic class definition is as follows:
public class UserReq {

    private long count;

    public long getCount(a) {
        return count;
    }

    public void addCount(a) {
        // To see the counting effect, loop through the method 1000 times
        for (int i = 0; i < 1000; i++) { count++; }}}Copy the code
  1. Test method: UsemainMethod is executed as follows.
public class UserReqTest {
    public static void main(String[] args) throws InterruptedException {
        int loopNum = 1000;
        UserReq userReq = new UserReq();
        for (int i = 0; i < loopNum; i++) {
            new Thread(userReq::addCount).start();
        }
        // The main thread sleeps for a while, waiting for all the child threads to complete.
        Thread.sleep(1000 * 5);
        System.out.println(userReq.getCount());
        // output: 999103. The output here is different every time I do it, but it's not 1000 by 1000}}Copy the code
  1. instructions:UserReqMedium counting methodcount++Is a compound operation that must be performed atomically to be thread-safe.

6.2 Improvement 1: Use the synchronized keyword

  1. Improvement methods:
    • inUserReqClass methodaddCountTo addsynchronizedThe keyword.
    • The lock of the synchronized code block is the object when the method is calleduserReq(created in the main method).
  2. Improved code: ignore
  3. Problem: Performance problem

6.2 Improvement: Use atomic variables to count

  1. Improvement methods:
    • inUserReqClass uses atomic variablesAtomicLongType instead of the underlying data typelong.
  2. Improved code:
public class UserReq {

    private final AtomicLong count = new AtomicLong(0);

    public long getCount(a) {
        return count.get();
    }

    public void addCount(a) {
        // To see the counting effect, loop through the method 1000 times
        for (int i = 0; i < 1000; i++) { count.incrementAndGet(); }}}Copy the code
// Main output: 1000000
Copy the code
  1. thinking:
    • There’s only one variable that you need to use atomic variables to coordinate state changes, if anyMultiple shared mutable variablesCan thread-safety requirements be met only by adding atomic variables?

    Maintaining state consistency requires updating all relevant state variables within a single atomic operation.

    • The main thread is not neededThread.sleep(1000 * 5)Wait for the thread to complete; You can useBinary atresiaReference,【Ch5- Basic Building blocks 】