Multithreading and high concurrency locking

Han Yu said such a sentence: “practice makes perfect shortage in one, success depends on forethought destroyed by”. Genius is nothing but endless diligence. Excellent and good performance; Wit and light, are doomed by diligence.

concept

process

A process is a running program that has a complete, private set of basic running resources. In general, each process has its own memory space.

A process is often seen as a synonym for a program or application; however, what a user sees as a single application may actually be a collection of processes that work together.

To facilitate communication between processes, most operating systems support interprocess communication (IPC), such as Pipes and Sockets. IPC not only supports communication on the same system, but also supports different systems. IPC communication modes include pipes (including nameless pipes and named pipes), message queues, semaphores, shared storage, sockets and Streams, among which Socket and Streams support two processes IPC on different hosts.

thread

Threads are sometimes referred to as lightweight processes. Both processes and threads provide an execution environment, but creating a new thread requires fewer resources than creating a new process.

Threads exist in processes – at least one thread per process. Threads share process resources, including memory and open files. This improves efficiency, but the potential problem is communication between threads.

Multithreaded execution is a fundamental feature of the Java platform. Every application has at least one thread – or several, if you count “system” threads, such as memory management and signal processing. But from the programmer’s point of view, only one thread is started, called the main thread.

In short: after a program runs, there is at least one process, and a process can contain multiple threads.

Concurrency and parallelism

  • Parallelism is when two or more events occur at the same time; Concurrency is when two or more events occur at the same time interval.
  • Parallelism is multiple events on different entities, and concurrency is multiple events on the same entity.
  • “Simultaneous” processing of multiple tasks on one processor, multiple tasks on multiple processors. Such as Hadoop distributed cluster

Thread safety

The basic concept

  1. What is a race condition: When two threads compete for the same resource, a race condition is said to exist if the order in which the resources are accessed is sensitive. The area of code that causes race conditions to occur is called a critical region. Race conditions can be avoided by using appropriate synchronization in critical sections, such as synchronized or locking mechanisms.

  2. What is thread-safe: Code that allows simultaneous execution by multiple threads is called thread-safe code. Thread-safe code does not contain race conditions.

Object security

Local primitive variables: Local variables are stored in the thread’s own stack. That is, local variables are never shared by multiple threads. Therefore, local variables of the underlying type are thread-safe. Here is an example of a local variable of the underlying type:

public class ThreadTest { public static voidmain(String[]args){ MyThread share = new MyThread(); for (int i=0; i<10; I ++){new Thread(share," Thread "+ I).start(); } } } class MyThread implementsRunnable{ public void run() { int a =0; ++a; System.out.println(Thread.currentThread().getName()+":"+a); Thread 0:1 thread 1:1 thread 2:1 thread 3:1 thread 4:1 thread 5:1 thread 6:1 thread 7:1 thread 8:1 thread 9:1Copy the code

No matter how many threads perform the ++a operation on the basic type A in the run() method, they update the value of the current thread stack without affecting other threads.

A local reference to an object is not quite the same as a local variable of the underlying type. Although the reference itself is not shared, the object to which the reference refers is not stored on the thread’s stack. All objects are stored in the shared heap. A method is thread-safe if an object created in that method does not escape (that is, it cannot be retrieved by other methods or referenced by non-local variables). In fact, even if you pass this object as an argument to another method, it is still thread-safe as long as it is not available to another thread.

Object members are stored on the heap. If two threads update the same member of the same object at the same time, the code is not thread-safe. If two threads are calling the same method on the same instance at the same time and have update operations, there is a race condition problem.

JAVA memory model

Communication between threads

Thread communication refers to the mechanism by which threads exchange information. In imperative programming, the communication mechanism between threads has two types of shared memory and messaging. Concurrency in Java is a shared memory model.

Java memory model structure

The Java Memory model (JMM), which determines when a write to a shared variable by one thread is visible to another thread. From an abstract point of view, JMM defines an abstract relationship between threads and main memory: Shared variables between threads are stored in main memory, and each thread has a private local memory where it stores copies of shared variables to read/write. Local memory is an abstraction of the JMM and does not really exist. It covers caches, write buffers, registers, and other hardware and compiler optimizations.

From the above figure, thread A and thread B must go through the following two steps in order to communicate:

  1. First, thread A flusher the updated shared variables from local memory A to main memory.
  2. Thread B then goes into main memory to read the shared variables that thread A has updated previously.

The lock

CAS optimistic locking

Optimistic locking: An operation is done without locking, assuming no conflicts, and retry until it succeeds if it fails because of conflicts. One typical implementation is Compare and Swap(CAS).

Ideologically, Synchronized is a pessimistic lock, pessimistic that the concurrency in the program is serious, so it is strictly guarded. CAS is an optimistic lock. It is optimistic that the concurrency in the program will be less serious, so it keeps the thread trying to update.

Disadvantages of CAS:

  1. High CPU overhead In the case of high concurrency, if many threads repeatedly try to update a variable but fail to update it all the time, the cycle will bring great pressure to the CPU.

  2. No Guarantee of atomicity of code blocks The CAS mechanism guarantees atomicity of only one variable, not the entire code block. For example, Synchronized is used to ensure that all three variables are updated atomically.

Synchronized blocks

concept

Synchronized blocks in Java are marked with synchronized. A synchronized block in Java is synchronized on an object. All synchronized blocks on an object can be accessed and operated on by only one thread at a time. All other threads waiting to enter the synchronized block are blocked until the thread executing the synchronized block exits.

Several ways to Synchronized blocks

  • Instance methods
  • A static method
  • Synchronization block in an instance method
  • Synchronized blocks in static methods

The above synchronization blocks are all synchronized on different objects. What kind of synchronized blocks are actually required depends on the situation.

Here is an instance method of synchronization:

Public class MethodSync {public synchronized void test(){try { System.out.println(thread.currentThread ().getName() + "test "); Thread.sleep(5000); System.out.println(thread.currentThread ().getName() + "test sleep ended "); } catch (InterruptedException e) { e.printStackTrace(); Public class MyThread extends Thread {@override public void run() {MethodSync sync = new MethodSync(); System.out.println(thread.currentThread ().getName() + "test ready to enter "); sync.test(); } } public class Test { public static void main(String[] args) { new MyThread().start(); new MyThread().start(); }} // thread-1 test is ready to enter // thread0 test is ready to enter // thread-1 test is ready to enter the synchronization method. // Thread0 test is ready to enter the synchronization method. // Thread0 test Is ready to enter the synchronization method //Thread-1 test Hibernation endsCopy the code
Public class MethodSync {public synchronized void test1(){try { System.out.println(thread.currentThread ().getName() + "test1 is in sync "); Thread.sleep(5000); System.out.println(thread.currentThread ().getName() + "test1 sleep ended "); } catch (InterruptedException e) { e.printStackTrace(); Public class MyThread extends Thread {static MethodSync sync = new MethodSync(); @override public void run() {system.out.println (thread.currentThread ().getName() + "test ready to enter "); sync.test1(); } } public class Test { public static void main(String[] args) { new MyThread().start(); new MyThread().start(); }} //Thread-0 test1 is ready to enter // thread-0 test1 is ready to enter //Thread-0 test1 is ready to enter //Thread-0 test1 is ready to enter //Thread-0 test1 sleep ends //Thread-1 test1 is ready to enter the synchronization method //Thread-1 test1 hibernation endsCopy the code

The synchronized keyword locks the current instance that calls the current method. If different instances are not affected by the synchronized keyword, the current method that is called by the same instance is constrained by the synchronized keyword.

Synchronized code block pass parameter variable object (lock variable object)
  • Only objects with the same property can be synchronized
Public class MethodSync public class MethodSync public class MethodSync public class MethodSync public class MethodSync public class MethodSync public class MethodSync public class MethodSync { public Integer lockObject; public MethodSync(Integer lockObject) { this.lockObject = lockObject; Public void test2() {synchronized (lockObject) {try {synchronized (lockObject) System.out.println(thread.currentThread ().getName() + "test2 enters sync method "); Thread.sleep(5000); System.out.println(thread.currentThread ().getName() + "Test2 sleep ended "); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class MyThread extends Thread { @Override public void run() { System.out.println(thread.currentThread ().getName() + "test ready to enter "); MethodSync sync = new MethodSync(127); sync.test2(); } } public class Test { public static void main(String[] args) { new MyThread().start(); new MyThread().start(); }} //Thread-0 test2 is ready to enter // thread-1 test2 is ready to enter // thread-0 test2 is ready to enter //Thread-0 test2 is ready to enter the synchronization method //Thread-0 test2 sleep ends //Thread-1 test2 is ready to enter the synchronization method Thread-1 test2 hibernation endsCopy the code

The member attributes of the same instance object must be the same. Here are examples of different instances, but synchronization is still achieved, for the following reasons:

Integer has A static cache. The value ranges from -128 to 127. If Integer A = 127 or Integer A = integer.valueof (127) is used, the cache is used. If you use Integer A = new Integer(127), it’s A new object every time. In this case, the member variable lockObject of the two object instances is actually the same object, so synchronization is achieved. There are also string constant pools to watch out for. So the concern here is whether the object passed by the synchronized code block is the same. This is actually the same way as the second way

Synchronizing code blocks to pass arguments to class objects (global locks)
  • All threads calling this method are synchronized
Public class MethodSync {// Global lock, Public void test3() {synchronized (methodsync.class) {try {synchronized (methodsync.class) System.out.println(thread.currentThread ().getName() + "test3 is in sync "); Thread.sleep(5000); System.out.println(thread.currentThread ().getName() + "Test3 sleep ended "); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class MyThread extends Thread { @Override public void run() { System.out.println(thread.currentThread ().getName() + "test ready to enter "); MethodSync sync = new MethodSync(); sync.test3(); } } public class Test { public static void main(String[] args) { new MyThread().start(); new MyThread().start(); }} //Thread-1 test3 is ready to enter // thread-1 test3 is ready to enter // thread-1 test3 is ready to enter // thread-1 test3 is ready to enter //Thread-1 test3 is ready to enter the synchronization method //Thread-1 test3 is ready to enter the synchronization method //Thread-1 test3 hibernation endsCopy the code
Modify static methods (global locks)

Static method locking is defined as Class object locking. Synchronized is defined as global locking.

Public class MethodSync {// global lock, Public static synchronized void test4() {synchronized (methodsync.class) {try { System.out.println(thread.currentThread ().getName() + "test4 is in sync "); Thread.sleep(5000); System.out.println(thread.currentThread ().getName() + "test4 sleep ended "); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class MyThread extends Thread { @Override public void run() { System.out.println(thread.currentThread ().getName() + "test ready to enter "); MethodSync.test4(); } } public class Test { public static void main(String[] args) { new MyThread().start(); new MyThread().start(); }} // thread-0 test4 is ready to enter // thread-1 test4 is ready to enter // thread-0 test4 is ready to enter the synchronization method //Thread-0 test4 sleep ends //Thread-1 test4 is ready to enter the synchronization method //Thread-1 test4 hibernation endsCopy the code

Synchronized, a static method, locks the class object on which the method is located. Because a class can correspond to only one class object, only one thread executes the static synchronized method in the class at the same time.

Ynchronized Upgrade process

A lock can be upgraded up, not down

When only one thread access, locking code for biased locking (biased locking is not lock, just hit a mark, mark the thread ID, each judge ID the same direct execution, greatly improve the performance, if the other thread will remove the tag, upgraded to CAS lock), when multiple threads to access at the same time will upgrade for lightweight locks, (by default, The number of spins is 10, which can be changed by using -xx :PreBlockSpin. Or if the number of threads waiting exceeds half of the number of cpus, the lock will be upgraded). When the upgrade condition is reached, the heavyweight lock will be upgraded, and the heavyweight lock will be queued and will not consume CPU resources.

The keyword Volatile

Volatile is the lightweight synchronized that guarantees the visibility of shared variables in a multiprocessor environment. It does not cause line-context switching and scheduling, and Volatile is used correctly and is cheaper to use and execute than synchronized.

Usage scenario: Read more than write

concept

Visibility: This refers to the visibility between threads. The modified state of one thread is visible to another thread. When one thread modifies a shared variable, another thread immediately sees it. For example, variables that are volatile are visible. Volatile variables do not allow in-thread caching and reordering (instruction reordering). Latile is not atomic or thread-safe.

The Java language provides the keywords volatile and synchronized to ensure order between threads. Volatile because it contains the semantics of “forbid instruction reordering.” Synchronized is acquired by the rule that a variable can only be locked by one thread at a time. This rule determines that two synchronized blocks holding the same object lock can only be executed serially.

The Java language provides a weaker synchronization mechanism, volatile variables, to ensure that updates to variables are notified to other routines. When a variable is declared volatile, both the compiler and the runtime notice that the variable is shared and therefore do not reorder operations on it with other memory operations. Volatile variables are not cached in registers or hidden from other processors, so volatile variables always return the most recently written value when read.

Access to volatile variables is not locked and therefore does not block the thread of execution, so volatile variables are a lighter synchronization mechanism than the sychronized keyword.

When a variable is defined as volatile, it has two properties:

  1. Visibility of a variable to all threads. Visibility, as described at the beginning of this article, is that when a thread changes the value of the variable, volatile ensures that the new value is immediately synchronized to main memory and flushed from main memory immediately before each use. This is not the case with common variables, whose values are passed from thread to thread through main memory.

  2. Let the reorder be optimized. For volatile variables, the “load ADDL $0x0, (% ESP)” operation is performed after the assignment. This operation acts as a memory barrier, which is not required when only one CPU accesses memory. Instruction reordering is a process that allows the CPU to divide multiple instructions in an unprogrammed order and send them to each circuit unit for processing.

Thread local variable

concept

The ThreadLocal class in Java allows us to create variables that can only be read and written by the same thread. Thus, if a piece of code contains a reference to a ThreadLocal variable, even if two threads execute the code at the same time, they cannot access each other’s ThreadLocal variables.

How do I create a ThreadLocal variable

The following code shows how to create a ThreadLocal variable:

private ThreadLocal myThreadLocal = new ThreadLocal(); // set the value mythreadlocal. set("A thread local value "); String threadLocalValue = (String) mythReadLocal.get ();Copy the code

ThreadLocal object. We only need to instantiate the object once, and we do not need to know which thread it is instantiated by. While all threads can access the ThreadLocal instance, each thread can only access the values it sets by calling ThreadLocal’s set() method. Even if two different threads set different values on the same ThreadLocal object, they still cannot access each other’s values.

About InheritableThreadLocal

The InheritableThreadLocal class is a subclass of the ThreadLocal class. Unlike a ThreadLocal, where each thread has its own value, InheritableThreadLocal allows a thread and all child threads created by that thread to access its saved value.