1: processes and threads

Process of 1.1:

Is a computer program about a data set on a running activity, is the system for resource allocation and scheduling of the basic unit, is the basis of the operating system structure.

1.2: thread

Is the smallest unit in which an operating system can schedule operations. It is contained within the process and is the actual operating unit within the process. A thread is a single sequential flow of control in a process, and multiple threads can be concurrent in a process, each performing a different task in parallel.

A process can have multiple threads, and the process has shared resources that can be called by internal threads. Thread switching is lighter than process switching.

1.3: Parallelism and concurrency

Concurrency: A core performs multiple tasks at a time, switching back and forth between these tasks. Only one task is being executed at a time.

Parallelism: There are multiple cores that can handle multiple tasks at the same time, and when there is parallelism, there is generally concurrency. Unless the number of tasks is less than or equal to the number of cores.

Serial: Each core performs tasks in serial.

1.4: Synchronous and asynchronous

  • Waiting for results to return before continuing is synchronization
  • To continue running without waiting for the result to return is different

Threads can be used in Java to make serial (synchronous) method execution asynchronous.

The efficiency of 1.5:

Using threads in multiple cores can improve efficiency

2: Java thread foundation

2.1: Creation of threads

2.1.1: Thread

Thread to create a thread and implement the run method

Inherit the Thread class or use anonymous inner classes

 public static void main(String[] args) {
            Thread t1=new Thread(Thread 1 "") {@Override
                public void run(a) {
                    for(int i=0; i<6; i++){ log.debug("running...."+i); }}}; t1.start(); log.debug("Main thread"); } output:10:58:02.097[thread1] DEBUG test - running.... 0
    10:58:02.097[main] DEBUG Tests the main thread10:58:02.100[thread1] DEBUG test - running...1.
    10:58:02.100[thread1] DEBUG test - running...2.
    10:58:02.100[thread1] DEBUG test - running...3.
    10:58:02.100[thread1] DEBUG test - running...4.
    10:58:02.100[thread1] DEBUG test - running.... 5
Copy the code

2.1.2: Runnable

Implement the runnable run method, pass the Runnable interface to thread

// Create a task object
Runnable task2 = new Runnable() {
 @Override
 public void run(a) {
 log.debug("hello"); }};// Parameter 1 is the task object; Parameter 2 is the thread name, which is recommended
Thread t2 = new Thread(task2, "t2"); t2.start(); Output:19:19:00 [t2] c.ThreadStarter - hello
Copy the code

Lambda compact code can be used in Java 8 later

Only one abstract method can use lambda

// Create a task object
Runnable task2 = () -> log.debug("hello");
// Parameter 1 is the task object; Parameter 2 is the thread name, which is recommended
Thread t2 = new Thread(task2, "t2");
t2.start();
Copy the code

2.1.3: FutureTask < >

The runnable interface is inherited and the Future interface is implemented. This interface supports the return of the result. The type of the generic type is the type to return the result.

Callable used with FutureTask:

The creation of FutureTask is passed a Callable

// Create a task object
FutureTask<Integer> task3 = new FutureTask<>(() -> {
 log.debug("hello");
 return 100;
});
// Parameter 1 is the task object; Parameter 2 is the thread name, which is recommended
new Thread(task3, "t3").start();
// The main thread is blocked, waiting for the result of task completion
Integer result = task3.get();
log.debug("Result :{}", result); Output:19:22:27 [t3] c.ThreadStarter - hello
19:22:27[main] c. tweestarter - Result:100
Copy the code

The get method

FutureTask’s get method is used to retrieve the return value and blocks until it succeeds.

2.2: How threads work

2.2.1: Stack and stack frame

2.2.2: Thread context switch

  • Thread switching is accompanied by stack frame switching

2.3: Common thread methods

2.3.1: Sleep method and state

  • The function is to sleep the thread, freeing the CPU, but not releasing the lock
  • Is a interruptible method that throws an exception
  • Threads are not necessarily executed immediately after sleep

Calling sleep causes the thread to enter a Time_waiting (blocked) state, such as viewing the state of the thread after it enters SLEPP

 Thread t1=new Thread(new Runnable() {
            @SneakyThrows
            public void run(a) {
                Thread.sleep(1000); }},"t1");
        t1.start();
        Thread.sleep(10); log.debug(String.valueOf(t1.getState())); Output:20:49:34.998[main] DEBUG test - TIMED_WAITINGCopy the code

Example: An example of an interrupted response under sleep

 Thread t1=new Thread(new Runnable() {
         @SneakyThrows
         public void run(a) {
             try{
                 Thread.sleep(2000);
             }catch (InterruptedException e){
                 log.debug("Interrupted response"); }}},"t1");
     t1.start();
     Thread.sleep(500);
     log.debug("Abort"); t1.interrupt(); Output:20:56:15.871[main] DEBUG Test - Interrupts20:56:15.873[T1] DEBUG Test - Response interruptCopy the code

2.3.2: Yield method

  • Relinquish CPU usage from running to runnable state

  • The specific implementation of task scheduler depends on the operating system

2.3.3: Thread priority

  • The bigger the priority is, the higher the priority is, but it’s just a suggestion, depending on the task scheduler.

  • Priorities are of little use when the CPU is idle

  • EtPriority You can set the priority

2.3.4: Join method

  • Wait for another thread to finish before executing this thread
  • Applied to synchronization of threads

The join method can pass in a unit of time as the maximum waiting time, beyond which it will not wait again

2.4: Thread interruption

2.4.1: Interrupt method

  • Can interrupt a thread, thread interrupt flag bit is set
  • A blocked thread can respond to an interrupt (for example, a thread executing sleep,wait, or Join) with a false interrupt flag

Example: Interrupting a blocked thread:

 Thread t=new Thread(new Runnable() {
            public void run(a) {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    System.out.println("Interrupted response");
                }
                System.out.println("Carry on"); }}); t.start(); Thread.sleep(1000);

        t.interrupt();
        Thread.sleep(100);
        System.out.println("Interrupt flag bit:"+t.isInterrupted()); Output: Continuation of interrupt flag bit in response to interrupt:false
Copy the code

The thread catches the interrupt and sets the thread interrupt flag to false

Interrupts when it is no longer blocking, but when it is blocking it responds as long as the flag interrupt is true

 Thread t=new Thread(new Runnable() {
            public void run(a) {
                System.out.println("Interrupting yourself in a non-blocking state.");
                Thread.currentThread().interrupt();
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    System.out.println("I can still catch this interrupt interrupt.");
                }
                System.out.println("Carry on"); }}); t.start(); Output: Interrupts itself in a non-blocking state and still catches the I interrupt and continues executionCopy the code

Example: Interrupt a normally running (non-blocking) thread:

 Thread t=new Thread(new Runnable() {
            public void run(a) {
               while (true){
                   System.out.println(Interrupt flag bit:+Thread.currentThread().isInterrupted());
                   System.out.println("running....."); }}}); t.start(); t.interrupt(); t.interrupt(); Output running... Interrupt flag bit:truerunning..... Interrupt flag bit:truerunning..... Interrupt flag bit:truerunning..... Interrupt flag bit:true.Copy the code

The thread just sets the interrupt flag bit to true and does not stop the thread, but it can terminate the thread by determining this flag bit

Application: Two-phase termination mode (gracefully terminating a thread)

Thread task;

  public void start(a){
      task=new Thread(new Runnable() {
          public void run(a) {
              while(true){
                  Thread t=Thread.currentThread();
                  if(t.isInterrupted()){
                      System.out.println("End thread");
                      break;
                  }
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                      System.out.println("In case of blocking state, reset flag bit."); t.interrupt(); }}}}); task.start(); }public void end(a){
      task.interrupt();
  }
Copy the code

2.4.2: Difference between the Interrupted method and isInterrupted method

  • Interrupted method: Checks for interruption but clears the flag
  • IsInterrupted: Checks for interruption but does not clear the flag

2.5: Main thread and daemon thread

By default, a Java process waits for all threads to finish running before terminating. There is a special thread called daemon thread that forces the daemon thread to terminate as soon as other non-daemons finish running, even if the daemon thread’s code has not finished executing.

  • The garbage collector thread is a daemon thread

Example: daemon threads

log.debug("Main thread running...");
        Thread t1 = new Thread(new Runnable() {
            @SneakyThrows
            public void run(a) {
                log.debug("Daemon thread starts running...");
                Thread.sleep(4000);
                log.debug("Daemon thread finished running..."); }},"daemon");
        // Set this thread as a daemon thread
        t1.setDaemon(true);
        t1.start();
        Thread.sleep(20);
        log.debug("Main thread finished..."); Output:12:24:55.865[main] DEBUG test - Main thread starts running...12:24:55.868[daemon] DEBUG Test - Daemon thread starts running...12:24:55.895[main] DEBUG test - Main thread running end...Copy the code

The daemon thread terminates as soon as any other thread terminates and does not execute the code that follows. For example, the above output does not print the completion of the daemon thread.

2.6: Thread status

2.6.1: Operating system level: Five states

  • The thread object is only created at the language level and is not yet associated with the operating system thread
  • Runnable state (ready state) Indicates that the thread has been created (associated with the operating system thread) and can be scheduled for execution by the CPU
  • Running status Indicates that the CPU time slice has been obtainedIn the operation of theThe state of the
    • When the CPU time slice runs out, it transitions from the Running state to the runnable state, causing a context switch for the thread
  • [Blocking state]
    • If a blocking API is called, such as BIO reading and writing files, then the thread will not actually use the CPU, causing the thread to switch context and enter the blocking state.
    • When the BIO operation is complete, the operating system will wake up the blocked thread and transition to the runnable state.
    • The difference with runnable is that the scheduler does not consider scheduling blocked threads as long as they remain awake
  • [Termination state] Indicates that the thread has finished executing and its life cycle has ended. It will not change to another state

2.6.2: Java layer: Six states

  • The NEW thread has just been created, but the start() method has not yet been called
  • RUNNABLE When the start() method is called, note that the Java API level RUNNABLE state covers the operating system level RUNNABLE state, running state, and blocking state. Still considered runnable)
  • BLOCKED, WAITING, and TIMED_WAITING are all subdivisions of BLOCKED state at the Java API level, as discussed in the state Transitions section below
  • TERMINATED when threaded code is TERMINATED

3: Shared model

3.1: Problems caused by sharing resources

  • Problems can occur when multiple threads operate on a shared resource simultaneously

3.1.1: Critical region

  • There is nothing inherently wrong with a program running multiple threads
  • The problem is multiple threads accessing a shared resource
    • There is no problem with multiple threads reading a shared resource
    • The problem occurs when instructions are interleaved while multiple threads are reading or writing to a shared resource
  • A block of code that contains multithreaded reads and writes to a shared resource is called a critical section

3.1.2: Race conditions

When multiple threads execute in the critical region, the result cannot be predicted due to the different execution sequences of the code, which is called a race condition

3.2: synchronized

3.2.1: Lock code blocks

It is mutually exclusive so that no more than one thread can hold the lock at a time, and any other thread trying to acquire the lock will block. This ensures that the thread that owns the lock can safely execute code within the critical zone without worrying about thread context switching

Grammar:

// Only one thread at a time can acquire the lock and enter the critical section
synchronized(object)// {critical region}Copy the code

The lock is not acquired and is blocked

Synchronized ensures atomicity of critical section code

3.2.2: Locks on methods

A static method

Lock the class

 public synchronized static void test(a) {}Copy the code

Nonstatic method

Lock the object

 public synchronized void test(a) {}Copy the code

3.3: Thread-safety analysis of variables

3.3.1: Member variables versus static variables

If it is shared and modified, it is not thread safe

3.3.2: Local variables

Local variables are thread-safe. However, security problems can occur if objects applied to local variables can escape the method (parameters passed in)

3.4: Mnitor concept

Java objects: Can be roughly divided into three parts, object headers, instance data, and alignment padding

3.4.1: Object header

Contains Mark Word and Pointers to classes (arrays also have array lengths)

Common objects:

|--------------------------------------------------------------|
|                   Object Header (64 bits)                    |
|------------------------------------|-------------------------|
|        Mark Word (32 bits)         |   Klass Word (32 bits)  |
|------------------------------------|-------------------------|
Copy the code

Array objects:

|---------------------------------------------------------------------------------|
|                                Object Header (96 bits)                          |
|--------------------------------|-----------------------|------------------------|
|          Mark Word(32bits)     |   Klass Word(32bits)  |   array length(32bits) |
|--------------------------------|-----------------------|------------------------|
Copy the code

3.4.2: Mark Word

The structure is as follows:

|-------------------------------------------------------|--------------------| | Mark Word (32 bits) | State | |-------------------------------------------------------|--------------------| | hashcode:25 | age:4 | biased_lock:0 | 01 | Normal (unlocked) | | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | | thread: 23 | epoch: | 2 Age: 4 | biased_lock: 1 | | 01 Biased (Biased locking) | | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | | ptr_to_lock_record: 00 30 | | lightweight Locked | |-------------------------------------------------------|--------------------| | ptr_to_heavyweight_monitor:30 | 10 | Weight Locked | | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | | | | 11 Marked for GC | |-------------------------------------------------------|--------------------|Copy the code

3.4.3: Principle of Monitor

Each Java object can be associated with a Monitor object, and if the object is locked (heavyweight) with synchronized, a pointer to the Monitor object is set in the Mark Word of the object’s header

3.5: Lock upgrade

3.5.1: Lightweight locks

JDK 1.6 introduced biased locking and lightweight locking, which enables locks to be in four states: unlocked, biasble, Lightweight locked, and heavyweight locking.

The following is the memory layout of the HotSpot VIRTUAL machine object header, which is called the Mark Word. The tag bits correspond to five states, which are given in the state table on the right. Except for the marked for GC state, the other four states have been described earlier.

On the left side of the figure below is a threaded virtual machine stack with a section called Lock Record, which is created during the Lightweight Lock run to hold the Mark Word for the Lock object. On the right is a lock object containing the Mark Word and other information.

Lightweight locks use the CAS operation to avoid the mutex overhead of heavyweight locks, as opposed to traditional heavyweight locks. For most locks, there is no competition during the synchronization cycle. Therefore, you do not need to use mutex for synchronization. You can use CAS for synchronization first.

If the lock object is unlocked and marked 0 01, the lock is unlocked. At this point, the virtual machine creates a Lock Record in the vm stack of the current thread, and then updates the Mark Word of the object to the Lock Record pointer using the CAS operation. If the CAS operation succeeds, the thread acquires the lock on the object, and the object’s Mark Word lock flag changes to 00, indicating that the object is in a lightweight lock state.

If the CAS operation fails, the virtual machine first checks whether the Mark Word of the object points to the vm stack of the current thread. If it does, the current thread already owns the lock object and can proceed directly to the synchronization block. Otherwise, the lock object has been preempted by another thread. If there are more than two threads competing for the same lock, the lightweight lock is no longer valid and should be inflated to a heavyweight lock.

3.5.2: Lock expansion

3.5.3: Spin optimization

Mutex synchronization entering a blocking state is expensive and should be avoided as much as possible. In many applications, shared data is locked for only a short period of time. The idea of a spinlock is to have a thread perform a busy loop (spin) for a period of time when it requests a lock that shares data, and if the lock is acquired during that time, it can avoid entering a blocking state.

Spin-locks reduce overhead by avoiding blocking, but they require busy loops that consume CPU time and are only suitable for scenarios where shared data is locked in a very short state.

If the spin fails, it is blocked

3.5.4: Biased locking

Important:

Both bias locks and lightweight locks are valid only if multiple threads do not acquire the lock at the same time, otherwise they are upgraded to heavyweight locks

The basic concept

The idea of biased locking is to favor the first thread to acquire the lock object so that the thread does not need to perform synchronization operations, or even CAS operations, after acquiring the lock.

When the lock object is first acquired by the thread, it enters the bias state marked 1 01. At the same time, the CAS operation is used to record the thread ID in Mark Word. If the CAS operation is successful, the thread does not need to perform any synchronization operation every time it enters the lock related synchronization block in the future.

Revoke Bias Is declared over when another thread attempts to acquire the lock object, and returns to the unlocked or lightweight lock state with Revoke Bias.

To state

Remember the Mark Word format

|-------------------------------------------------------|--------------------| | Mark Word (32 bits) | State | |-------------------------------------------------------|--------------------| | hashcode:25 | age:4 | biased_lock:0 | 01 | Normal (unlocked) | | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | | thread: 23 | epoch: | 2 Age: 4 | biased_lock: 1 | | 01 Biased (Biased locking) | | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | | ptr_to_lock_record: 00 30 | | lightweight Locked | |-------------------------------------------------------|--------------------| | ptr_to_heavyweight_monitor:30 | 10 | Weight Locked | | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | | | | 11 Marked for GC | |-------------------------------------------------------|--------------------|Copy the code

Biased locking is enabled by default, but with a delay.

When an object is created:

  • If biased locking is enabled (by default), the object is created with a markword value of 0x05 (the last three bits are 101), and its thread, epoch, and age are 0
  • Biased locking is the default is delay, not in the startup program takes effect immediately, if you want to avoid delays, can add VM arguments – XX: BiasedLockingStartupDelay = 0 to disable delay
  • If biased locking is not enabled, then the object is created with the markword value 0x01 (the last three bits are 001), and its hashcode and age are 0

Note that this is only going into a biased state, not locked

Biased understanding:

/ / add virtual machine parameters - XX: BiasedLockingStartupDelay = 0
public static void main(String[] args) throws IOException {
 Dog d = new Dog();
 ClassLayout classLayout = ClassLayout.parseInstance(d);
 new Thread(() -> {
 log.debug("Before the synchronized");
 System.out.println(classLayout.toPrintableSimple(true));
 synchronized (d) {
 log.debug("Synchronized");
 System.out.println(classLayout.toPrintableSimple(true));
 }
 log.debug(After the "synchronized");
 System.out.println(classLayout.toPrintableSimple(true));
 }, "t1").start(); Output:11: 08:58.117 c.TestBiased [t1] - synchronized00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000101 
11: 08:58.121 c.TestBiased [t1] - synchronized00000000 00000000 00000000 00000000 00011111 11101011 11010000 00000101 
11: 08:58.121 c.TestBiased [t1] - synchronized00000000 00000000 00000000 00000000 00011111 11101011 11010000 00000101 
Copy the code

Pay attention to

After an object in a biased lock is unlocked, the thread ID is still stored in the object header

This is the meaning of bias, bias to a thread, the next time the thread needs to acquire the lock can directly enter (only the first cas operation). However, if there is a contention, the biased state will be broken, such as another thread trying to acquire the lock, but realizing that the thread ID is not its own. Revoke Bias then restores to the unlocked or lightweight locked state.

Disable bias lock

Add the VM parameter -xx: -usebiasedLocking to disable biased locking when the test code above runs

Obtain hashCode ()

A normal-state object starts out without hashCode and is generated by the first call

The hashCode of the object is called, but the biased lock object MarkWord stores the thread ID. If calling hashCode causes the biased lock to be revoked, there will be no biased lock state

  • Lightweight locks record hashCode in the lock record
  • Heavyweight locks record hashCode in Monitor

Lock upgrade: Partial lock -> Lightweight lock

Note that the lock is upgraded to a lightweight lock only when two threads do not acquire the lock at the same time. Otherwise, the lock is upgraded to a heavyweight lock

  • T1 thread acquires the lock at time T1, which is biased lock, and releases the lock at time T3
  • T2 thread tries to acquire the lock at T4, and finds that the lock is biased lock, and the thread ID is not its own, so the biased lock is invalid and upgraded to lightweight lock
  • But what if the T2 thread tries to acquire the lock at time T2? Then it will upgrade to a heavyweight lock

When wait/notify is called

This must be upgraded to a heavyweight lock because only Monitor has a WAIT queue.

Bulk bias

If the object is accessed by multiple threads, but there is no contest, then the object biased to Thread T1 can still be biased to Thread T2 again. Rebiased resets the Thread ID of the object

After the threshold of unbiased locking exceeds 20 times, the JVM will say, “I’m biased wrong,” and will re-bias these objects to the locked thread when they are locked

Note that locking is not for objects, but for classes

Batch cancellation

When the bias lock threshold is revoked more than 40 times, the JVM feels that it is indeed biased in the wrong way and should not have been biased at all. Then all objects of the entire class will become unbiased, and so will the new objects

2.6.2: eliminate the lock

Lock elimination refers to the elimination of locks on shared data that are detected to be impossible to compete with.

Lock elimination is mainly supported by escape analysis. If shared data on the heap cannot escape and be accessed by other threads, it can be treated as private data and thus unlocked.

3.5.6: Lock coarsening

For example, if a loop operation repeatedly locks the same object, the JVM extends the lock to the entire loop

3.6: wait and notify

3.6.1 track:

Note:

The wait method enters the WAIT queue, but after notify wakes up, it enters the entry queue and waits for the lock.

A call to wait() causes a thread to wait for a condition to be met. The thread is suspended while waiting. When another thread runs such that the condition is met, the other thread calls notify() or notifyAll() to wake up the suspended thread.

They are all part of Object, not Thread.

Can only be used in synchronous method or synchronous control block, (which is already gets the lock, no lock cannot use these methods) or you’ll throw IllegalMonitorStateException at runtime.

During a wait() suspension, the thread releases the lock. This is because if the lock is not released, no other thread can enter the synchronization method or synchronization control block of the object, and there is no notify() or notifyAll() to wake up the suspended thread, resulting in a deadlock.

3.6.2: API introduction

  • obj.wait()Letting the thread that enters the Object monitor wait on waitSet releases the lock
  • obj.notify()In the thread waiting on waitSet on objectPick a wake up
  • obj.notifyAll()Let the thread on object wait on waitSetAll wake up

3.6.3: Finite wait

The wait method can pass in a time to indicate the maximum wait time

3.6.4: Sleep and wait and enter states

The difference between

  • Sleep is the Thread method, and wait is the Object method
  • Sleep is not mandatory with synchronized, but wait is mandatory with synchronized
  • Sleep does not release object locks while sleeping, but wait releases object locks while waiting

state

  • The sleep method enters TIMED_WAITING
  • Wait Indicates that the state is WAITING
  • Wait (timed parameter) enters TIMED_WAITING state

3.7: Thread state transition

It should all be in runnable state, and it should all be blocked later.

3.8: a deadlock

Two threads hold each other’s locks and wait for each other to release them, something like that

Lock 3.8.1: live

A live lock occurs when two threads change each other’s termination conditions so that neither thread can terminate, for example

public class TestLiveLock {
 static volatile int count = 10;
 static final Object lock = new Object();
 public static void main(String[] args) {
 new Thread(() -> {
 // The expectation drops to 0 to exit the loop
 while (count > 0) {
 sleep(0.2);
 count--;
 log.debug("count: {}", count); }},"t1").start();
 new Thread(() -> {
 // Exit the loop when expectations exceed 20
 while (count < 20) {
 sleep(0.2);
 count++;
 log.debug("count: {}", count); }},"t2").start(); }}Copy the code

3.8.2: hunger

A thread that has not been executed for a long time

3.9: already

ReentrantLock Notes link

4: Java memory model

Look at the document

JMM is the Java Memory Model, which defines the main Memory, working Memory abstraction, the underlying corresponding CPU registers, cache, hardware Memory, CPU instruction optimization, etc.

The JMM is embodied in the following aspects

  • Atomicity – Ensures that instructions are not affected by thread context switching
  • Visibility – Ensures that instructions are not affected by the CPU cache
  • Order – Ensures that instructions are not affected by parallel optimization of CPU instructions

4.1: visibility

Use volatile and SYN

4.2: atomicity

Volatile does not guarantee atomicity

4.3: Orderliness – Instruction rearrangement

Use volatile to disallow instruction reordering