java concurrent programming foundation - thread-safe

Thread Safety

Thread safety on behalf of the correctness of concurrent programs, referring in a multithreaded environment, the application will always be able to demonstrate correct behavior.

Root of the problem

All of thread safety issues, can be attributed to the same cause: the state of shared variable. "Share" means that the variable can be accessed simultaneously by multiple threads, "variable" means that the value of the variable changes can occur during its life cycle.
Writing thread-safe code that is core to shared mutable state access operations are managed by introducing synchronization mechanism to ensure that only one thread at any time access to shared mutable state.

Shared mutable object

In concurrent programming, the shared variable objects will face the following three questions:

Atomicity

Race Conditions

When the accuracy of a calculation depends on the order of a plurality of threads are alternately performed, so a race condition can occur. Common two kinds of static mode:

  • read-modify-write (read - change - write) operation that refinement steps of: reading a shared variable value (Read), and then doing calculation (Modify) based on the value, then updates the value (write) shared variable .
  • check-then-act (after monitoring operation), refinement steps of: reading a value of the shared variable, the value of this variable determines what the next action in accordance with Yes.

read-modify-write of example:

@NotThreadSafe
public class UnsafeCounter {
  private int value;

  public int getNext() {
      return value++;
  }
}
复制代码

Wherein the value increment operator ++ comprises three steps: Read the value of value, plus the value of 1, the final calculation result is written to the vlue value, typical read rewrite race mode.
check-then-act example:

@NotThreadSafe
public class UnsafeSequence {
  private int sequence;
  
  public int getSequence() {
      if(sequence >= 99){ // 步骤1 检查共享变量
          return 0;      // 步骤2 act 检查后操作
      } else {
          return sequence++;
      }
  }
}
复制代码

How to avoid the race condition problem, need to introduce atomic operations to ensure that certain threads in the modification process of shared variables, other threads can not operate, the other thread can read and modify the state of a shared variable before or after the atomic atomic operation. Here we look at what is an atomic operation:

Atomic operation

Operation involving access to shared variables, if the operation performed from any thread other than the thread of view are inseparable, then the operation atomic operation, the operation is atomic. From the concept of see note the following points:

  • Atomic operation is for the purposes of access to the shared variable operation, it does not matter whether the atom for local variables
  • Atomic operation is for multi-threaded environment makes sense

At the same time indivisible following two meanings:

  • Access (read, write) a shared variable from a thread other than the thread of execution point of view, this operation has been performed either ended or has not happened yet, do not exist in this state
  • Access unified set of shared operating variables that can not be staggered.

java atomicity of two ways:

  1. Use lock (Lock) has the exclusive lock that can engage in to protect a shared variable at any time only one thread can be accessed (about lock will have to share a single chapter).
  2. Use CAS instruction, CAS command directly in hardware processor and memory to achieve this level, can be seen as a hardware lock (CAS will share a separate chapter).

Visibility

In a multithreaded environment, after a thread of a shared variable is updated, follow-up visits to the variable thread may not be able to immediately read the updated results, even permanently unable to read the updated results. This is another manifestation thread-safe: visibility.
The visibility of the problem is due to the decision of java memory model, specific reference java concurrent programming - memory model
java platform how to ensure visibility:

  • volatile keyword is used to ensure that the notification to update the operating variables of another thread
  • Locking mechanism can ensure both visibility and can ensure atomicity
  • final Visibility: The final modification of the fields in the constructor Once completed, then other threads can see the final field value

Orderliness

Modern microprocessor instructions executed out of order by the (out-of-order execution) to improve the efficiency, in addition to the processor, Java JIT editor itself will do reorder instructions, machine instructions may be finally generated byte code sequence is not consistent. Concurrent program, reorder instruction execution may cause unexpected results, such as the following program, in the multi-threaded execution threads in a statement may be executed out of order, flg = true may precede a = 1 is executed, the thread 2 may print out unexpectedly a = 2.

How java platform to ensure sequential memory accesses:

  • volatile keyword to disable the processor reordering by adding a specific type of memory barrier instructions
  • Lock to prohibit reordering

Solve thread safety issues

Avoid sharing state

Stateless objects must be thread-safe, typical Servlet program, each Servlet itself does not hold state, isolated from each other, they do not interfere with each other. If the hold state is inevitable, you can use thread sealing technique, the state of the "hidden" to prevent access to other threads. Common approach is to stack closed and ThreadLoca two forms. Closed thread stack is a special case closed, closed in the stack, local variables can only be accessed through the object, these local variables are enclosed within the stack of execution threads, other threads can not access to them.

    public int loadTheArk(Collection<Animal> candidates) {
        SortedSet<Animal> animals;
        int numPairs = 0;
        Animal candidate = null;

        // animals confined to method, don't let them escape!
        animals = new TreeSet<Animal>(new SpeciesGenderComparator());
        animals.addAll(candidates);
        for (Animal a : animals) {
            if (candidate == null || !candidate.isPotentialMate(a))
                candidate = a;
            else {
                ark.load(new AnimalPair(candidate, a));
                ++numPairs;
                candidate = null;
            }
        }
        return numPairs;
    }
复制代码

In the above code, animals and local variables of the candidate is to be confined within the stack frame, does not escape, to be accessed by other threads, this method is thread safe. There will be a special section on ThreadLocal introduced, there is not expanded explanation.

Avoid mutable state

If an object whose state can not be modified after it is created, then the object is called immutable objects. Immutable objects must be thread-safe. When the following conditions are met, the object is immutable:

  • After the object is created that the state can not be modified
  • All properties are final type Object
  • Objects are created correctly (during the creation, this refers not escape)

Guava library also provides a set of immutable class, such as ImmutabelList, ImmutableSet these, we should use them in your code as much as possible

Synchronization mechanism

If the shared variable and can not be avoided, then only use worst - synchronization mechanism to ensure thread safety. In Java code, usually synchronized keyword, the class or object locking, to achieve synchronization. Java-specific synchronization mechanism which will analyze in the next chapter.

Guess you like

Origin juejin.im/post/5d6a804f51882571532c98ae