Multi-threaded (JUC) notes summary

 

table of Contents

1. First acquainted with threads (memory model, three characteristics, reordering)

Thread related concepts

Second, the creation of threads

Thread life cycle

Three, lock

The concept of lock

Lock upgrade

Object header

Pessimistic lock synchronized

Synchronized lock reentry

synchronized can not be interrupted

Principle of synchronized


1. First acquainted with threads (memory model, three characteristics, reordering)

Thread related concepts

When it comes to threads, one must say processes. Processes are the basic unit of system allocation and application. Processes can be understood as a program unit that can run independently, just like opening a QQ is a process (multiple QQs can be opened at the same time, allowing multiple processes? ), then a process can contain multiple threads. For example, opening a QQ chat window is one thread (if it is a chat merge window, there is only one thread). Using single thread, there will be a waiting situation when performing a long operation. Under the condition of multi-core CPU, in order to improve the utilization of the CPU, multiple threads can be used to perform other operations to reduce time-consuming.

important point:

1. The use of multithreading consumes system resources, because multithreading needs to open up memory, and thread switching also takes time. (Multi-threaded processing leads to 80% of the production memory usage alarm)

2. The termination of the thread will affect the program

3. There is shared data between multiple threads, which is prone to thread deadlock

The concept of main thread and child thread

The main thread is like the main thread, and the child thread is the thread started in the main method.

Randomness of multi-threaded operation

Due to the problem of CPU time slices, among multiple threads, whoever grabs the CPU resources first will execute first.

Context switch

The processor needs to consume resources to switch from one thread to another.

Thread safe

That is, when multiple threads modify the value of the same variable in the same object, confusion may occur, resulting in a situation where the value is not synchronized.

Atomicity

Either success or failure, there can be no half of the success. Implementation scheme: use synchronized lock or CAS

Visibility

Whether a thread modifies a variable in an object is visible to another thread. Volatile solves the problem of visibility and order

Orderliness

Under the condition of a multi-core processor, the memory operation sequence and code sequence may be inconsistent. (Not necessarily appearing). Volatile solves the problem of visibility and order, and synchronized

Reorder

When the compiler is compiling, the code order may be inconsistent with the compiled order. (Not necessarily appearing).

Instruction reordering: caused by the JIT compiler and processor, the code order is inconsistent with the execution order.

Storage system reordering: caused by caches and write buffers, the perception order is inconsistent with the execution order.

Memory operation

load read memory. Read data from memory to register.

store write memory. Write the data to the memory unit at the specified address.

Loadload (reading) reordering, loadstore (reading and writing) reordering, etc., which means that it may be executed first (reordering) later

Seemingly serial semantics

Ensure that reordering does not affect the execution order of a single thread.

Memory model

1) Each thread has an independent stack space

2) Every thread can access heap memory

3) The CPU does not directly read the data in the main memory. When the CPU reads the data, it first reads the main memory data into the cache, and then reads the data in the cache into the CPU register.

4) Shared data in JVM may be allocated to different CPU registers, which will cause invisibility between threads.

The Java memory model can be abstracted as: working memory and main memory.

Second, the creation of threads

Implement runnable, inherit Thread, implement callable

//方式一
public class MyThread  implements  Runnable{
    @Override
    public void run() {
        System.out.println("this is run");
    }
 
    public static void main(String[] args) {
        new Thread(new MyThread()).start();
    }
}
//方式二:
public class MyThread extends  Thread{
    @Override
    public void run() {
        System.out.println("this is run");
    }
 
    public static void main(String[] args) {
        new Thread(new MyThread()).start();
    }
}
//方式三:
public class ExcutorsCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "test";
    }
public static void main(String[] args) {
        Thread t = new Thread(new MyThread());
    t.start();
 }
 }

What is the difference between the execution thread start and run?

 The start method is to start a thread, and the run is just to execute an ordinary method. 

Thread life cycle

Contains 5 stages, including: new, ready, running, blocking, and destroying.

New: the thread that has just used the new method and new comes out;
Ready: After the start() method of the called thread, the thread is in the stage of waiting for the CPU to allocate resources. Whoever grabs the CPU resources first will start the execution;
Run: When When a ready thread is scheduled and obtains CPU resources, it enters the running state. The run method defines the operation and function of the thread;
blocking: When in the running state, the thread in the running state may become blocked for some reason. For example, the thread is in a blocked state after sleep() and wait(). At this time, other mechanisms are needed to wake up the blocked thread, such as calling the notify or notifyAll() method. The awakened threads will not execute the run method immediately. They will wait for the CPU to allocate resources again and enter the running state;
destruction: If the thread is executed normally or the thread is forcibly terminated in advance or ends due to an exception, the thread will be destroyed Release resources;

When does the thread end?

1. The run method is executed normally

2. The run method throws an exception

It is recommended to implement the Runnable interface to develop multithreading, because Java single inheritance but can implement multiple interfaces.
However, these two implementations are rarely used in actual development, and can only do simple testing and debugging at most. More is the use of thread pools.
"Alibaba Java Development Manual" also emphasized in the first chapter, section 6, concurrent processing, that "thread resources must be provided through the thread pool, and it is not allowed to display the creation of threads in the application."

Three, lock

The concept of lock

Pessimistic lock : Ensure that resources are locked for every operation. synchronized

Optimistic locking : does not directly lock resources, through CAS (compare and swap) + spin

Internal locks (built-in locks) : Every java object can be used as a synchronization lock, and these locks become built-in locks. When a thread enters a synchronized code block or method, it will automatically acquire the lock, and when it exits a synchronized code block or method, it will release the lock. The only way to obtain a built-in lock is to enter the synchronized code block or method protected by this lock.
The built-in locks are mutex locks.

synchronized, it is a built-in keyword of the JVM. Every object in java has a lock, also called a monitor, which is an exclusive lock.

The principle of synchronized is that the JVM instructs monitorenter and moniterexit to lock and release

Exclusive lock : can only be obtained by one thread.

synchronized(lock){
}

The lock is called the lock object. Note that if the lock is this, then if the two threads operate on different lock objects, they will not be synchronized. Because they hold different locks.

You can use constants as lock objects.

Object lock : Each instance of a class corresponds to a lock, and the object used as a lock is called an object lock. When there is a synchronized modified method or code block in an object, if you want to execute this code, you must first obtain the object lock

Fair lock : When multiple threads apply for the same resource, they must acquire the resources one by one in the order of application.

Unfair lock : When the resource is released, any thread has a chance to obtain the resource, regardless of the order of its application.

Mutual exclusion lock (synchronization) : When synchroinzed is used to lock multiple different code fragments, but the synchronization monitor object used by these synchronized blocks is the same, then these code fragments are mutually exclusive. Multiple threads cannot execute them at the same time.

Synchronization lock : Synchronization is ABCD these threads have to agree on a coordinated order of execution. For example, if D is to be executed, both B and C must be completed, and for B and C to begin, A must be completed first.

Exclusive lock (write lock) : means that the lock can only be held by one thread at a time. Both ReentrantLock and Synchronized are exclusive locks.

Shared lock (read lock) : Refers to the lock can be held by multiple threads.

For ReentrantReadWriteLock, its read lock is a shared lock, and its write lock is an exclusive lock. The sharing of read locks can ensure that concurrent reading is very efficient. The processes of reading, writing, writing, and writing are mutually exclusive.

Shared locks, exclusive locks, and exclusive locks are an implementation of pessimistic locks

Spin lock : The spin lock can make the thread not be suspended when the lock is not obtained, but turn to execute an empty loop (the so-called spin is to execute the empty loop by itself), if it is in several empty loops Later, if the thread can obtain the lock, it continues execution. If the thread still cannot obtain the lock, it will be suspended.

After using the spin lock, the probability of the thread being suspended is relatively reduced, and the continuity of thread execution is relatively enhanced. Therefore, for those concurrent threads where the lock competition is not very fierce and the lock occupying time is very short, it has a certain positive meaning, but for the concurrent program where the lock competition is fierce and the single-threaded lock occupying a long time, the spin lock is after the spin wait. , Often resolutely unable to obtain the corresponding lock, not only wasted CPU time, but ultimately inevitably suspended operations, but wasted system resources.

Blocking lock : Let the thread enter the blocking state to wait. When the corresponding signal (wake up, time) is obtained, it can enter the ready state of the thread. All threads in the ready state enter the running state through competition. .
In JAVA, the methods that can enter\exit, block state, or contain block locks include the synchronized keyword (weight lock among them), ReentrantLock, Object.wait()\notify()

Reentrant locks (avoid deadlocks) : Reentrant locks, also called recursive locks, mean that after the outer function of the same thread acquires the lock, the inner recursive function still has the code to acquire the lock, but it is not affected.
ReentrantLock and synchronized are both reentrant locks in the JAVA environment

Lock upgrade

Object header

Object = object header + object body + alignment byte (padding byte)

Object header = Mark Word + Kclass Word + array length

The role of several parts of the object:

1. The Mark Word in the object header is mainly used to indicate the thread lock status of the object, and it can also be used to cooperate with the GC and store the hashCode of the object;

2. Klass Word is a pointer to the Class information in the method area, which means that the object can know which Class instance it is at any time;

In each object instance, there is an object header. There are MarkWord and Kclass in the object header. There are 2 flags in MarkWord to identify the state of the lock. They are: unlocked state-"biased lock-"lightweight lock-" Heavyweight lock

There are 5 types of object states: normal, biased lock, lightweight lock, heavyweight lock, GC mark

The object is composed of an object header, an object body, and padding bytes.
The object header consists of Mark Word and Kclass, and the length of the array.
MarkWord puts the thread lock status and the information (hashcode) required by Gc.
Kclass is a pointer to an instance of the method area.

JFK1.6 previously used blocking heavyweight locks. The Hotspot authors found that not every time there was thread competition, so they introduced biased locks.

The process of lock upgrade:

1.假设刚开始A线程获取锁的时候第一次使用CAS操作来获取偏向锁;并且会把线程ID存到对象头的MarkWord和栈帧里面。

2.第二个B线程来了检验对象头MarkWord是不是自己拥有这把偏向锁,也就是查看是否有当前线程ID,如果是直接使用。

如果不是,会去检查MarkWord锁标识位是不是1(1表示是偏向锁),如果不是1,说明前一个线程释放了,则通过CAS操作来获取锁。

如果锁标识位是1,再进行一次CAS加锁尝试把偏向锁指向自己,如果失败(因为A线程正在使用当中并且A程还存活,发生锁竞争)则将锁升级成为轻量级锁;

3.升级成为轻量级锁之后,线程B会使用自旋的方式来获取这个锁,自旋也就是循环尝试,如果B通过自旋的方式也一直获取不到锁,那么锁将会升级成为重量级锁,线程通过阻塞等待获取资源。

4.重量级锁是依赖对象内部的monitor锁来实现的,而monitor又依赖操作系统的MutexLock(互斥锁)来实现的,所以重量级锁也被成为互斥锁。

Lock elimination : Lock elimination means deleting unnecessary locking operations. According to the code escape technique, if it is determined that the data on the heap will not escape the current thread in a piece of code, then this piece of code can be considered to be thread-safe, and no locking is necessary.


Pessimistic lock synchronized

Synchronized lock reentry

Lock reentrance: The same thread holds the same lock object and can repeatedly enter the synchronization code block.

//1.递归
public class SynchronizedTest {
    public synchronized  void test1() throws InterruptedException {
        System.out.println("this is test1");
        TimeUnit.SECONDS.sleep(3);
        test1();
    }


    public static void main(String[] args) throws InterruptedException {
        SynchronizedTest synchronizedTest = new SynchronizedTest();
        synchronizedTest.test1();
    }
}
//不同方法
public class SynchronizedTest {
    public synchronized  void test1() throws InterruptedException {
        System.out.println("this is test1");
        test2();
        TimeUnit.SECONDS.sleep(3);

    }
    public synchronized  void test2() throws InterruptedException {
        System.out.println("this is test2");
        TimeUnit.SECONDS.sleep(3);
   }


    public static void main(String[] args) throws InterruptedException {
        SynchronizedTest synchronizedTest = new SynchronizedTest();
        synchronizedTest.test1();
    }
}

The effect of synchronized on the method is equivalent to holding the lock of the current object. Therefore, when test1 is called, the current object is locked, but test2 can still be entered.

Reentrant principle: counter for the number of locks

1. The JVM is responsible for tracking the number of times the object is locked;

2. There is a monitor counter. When a thread locks an object for the first time, the count becomes 1. Whenever the same thread acquires a lock on this object again, the count will increment.

3. When the task ends and leaves, monitorexit will be executed, and the count will be decremented until it is completely released

synchronized can not be interrupted

Once the lock has been acquired by another thread, if the current thread still wants to acquire it, it can only choose to wait or block until another thread releases the lock. If other threads never release the lock, then the thread can only wait forever.

In contrast, the Lock class can have the ability to interrupt.

Principle of synchronized

Disassembled with javap, you can see that there is a JVM instruction monitorenter and moniterexit to lock and release. When an exception occurs, it will go to another moniterexit.

monitorenter: Each time a thread enters, the counter is +1. If you re-enter, continue to add. A counter greater than 0 means that there is a thread holding the lock, and other threads cannot enter.

monitorexit: When the thread exits, when the counter -1 becomes 0, other threads can acquire the lock.

Guess you like

Origin blog.csdn.net/x18094/article/details/115024430