1. Volatile keyword

The JDK provides a lightweight synchronization mechanism. It guarantees visibility, order, not atomicity

1.1 What is Visibility

In the JMM model, when a thread manipulates a variable in main memory, it first copies a variable into the thread’s working memory, and then updates the value of the variable into main memory when the update is complete.

When multiple threads operate, values read between different threads may not be updated in real time. Therefore, we need to provide a mechanism to notify other threads that the value of the variable has been updated when a thread updates it, and retrieve the value of the variable from main memory.

The volatile keyword provides this mechanism. When a variable is modified by the volatile keyword, the value of the variable is modified and re-read by other threads.

Code demonstration visibility:

public class VolatileDemo {
    private volatile int num = 1 ;

    public static void main(String[] args) {
        VolatileDemo volatileDemo = new VolatileDemo();
 new Thread(()->{try {  TimeUnit.SECONDS.sleep(3); }catch(InterruptedException e) {e.printStackTrace(); } volatileDemo.num = 10 ;  }).start();  while(volatileDemo.num == 1) {  }  System.out.println("The value has been changed to :"+volatileDemo.num);  } } Copy the code

When num is changed to 10, the main thread jumps out and prints. Implement visibility.

1.2 What is atomicity

Consider atomicity in database transactions first. All or none of the SQL in a transaction can succeed or fail.

Atomicity in Java, atomic operations cannot be blocked or interrupted within a thread.

Atomicity demonstration is not guaranteed

public class VolatileDemo {
    private volatile int num =0 ;

    public void addPlus(a){
        num++;
 }  public static void main(String[] args) {  VolatileDemo volatileDemo = new VolatileDemo();  for(int i = 0 ; i < 20 ; i++){  new Thread(()->{  for (int j = 0; j < 1000; j++) {  volatileDemo.addPlus();  }  },"The thread is:"+i).start();  }  By default, there are two threads in the background, one main and one GC  while(Thread.activeCount() > 2) { Thread.yield();  }  System.out.println(Thread.currentThread().getName()+"\t finally\t"+volatileDemo.num);  }   // Output main finally 19689 Copy the code

Why this happens:


Let’s say thread 1 is doing num++, and when it needs to write to main memory after it has done the ++ operation, the thread is suspended and thread 2 executes.

Thread 2 is about to notify other threads that the value is to be changed. Thread 1 is about to change the value to 1 again, causing a add failure.

num++The underlying implementation

Obtained by decompilation

  2: getfield      #2                  // Field num:I
       5: iconst_1
       6: iadd
       7: putfield      #2                  // Field num:I
Copy the code

First get the field, then get a constant value of 1, add the field, write to main memory

This is what happens in this step.

Solution does not guarantee atomicity

By adding locks

Through atomic classes

/ * ** Atomic classes implement atomicity* /
class MyData{
    // Default is 0
 volatile AtomicInteger atomicInteger = new AtomicInteger();   public void addPlus(a){  / / + + first  atomicInteger.incrementAndGet();  / / to get first // atomicInteger.getAndIncrement();  } } public class atomicDemo {  public static void main(String[] args) {  MyData myData = new MyData();  for(int i = 0 ; i < 20 ; i++){  new Thread(()->{  for (int j = 0; j < 1000; j++) {  myData.addPlus();  }  },"The thread is:"+i).start();  }  while(Thread.activeCount() > 2) { Thread.yield();  }  System.out.println(Thread.currentThread().getName()+"\t value:\t"+myData.atomicInteger.get());  } } Copy the code

1.3 What is order

Again, first recall that there is an optimizer in the MySql architecture that optimizes the developer’s own SQL statements. That is, the MySql engine does not follow the logic written by the developers themselves, but instead has an optimization process.

Similarly, Java has a mechanism where when multiple threads are executing at the same time, the CPU does not necessarily execute tasks in the order written by the developer. At this point, the order of code execution is thrown out of order. Code order is not guaranteed at this point.

But there’s a mechanism in Java for directive reordering where you can’t have dependencies

Code interpretation:

int x = 11 ;     / / 1
int y = 12 ;    / / 2
x = x+5;       / / 3
y = y*x;      / / 4
Copy the code

The order of execution in the code might appear:

  • 1234
  • 2134
  • 1324
  • 1243 or 3421 will not occur because there is no dependency between the data because the instruction reordering is required to follow the rules.

Code reading

There are three steps to creating an objectFirst, allocate a memory area in the heap/ / 1
Create objects in this memory region/ / 2
Assigns the address of this memory region to the variable of this object/ / 3
Copy the code

If volatile is not added, the order of execution is 132 (there is no dependency between 2 and 3), and the address of the object read by the thread is null.

Both cases were errors caused by reordering orders

1.4 How is order and visibility implemented for Volatile

At the bottom is the memory barrier.

Memory barriers serve two purposes: first, they ensure the order in which certain operations are executed. Second, make certain variables visible in memory


1.5 DCL

Double Check Lock (DCL) Double Check Lock.

Take a look at the implementation of the singleton pattern:

class MyInstance {
    private static volatile MyInstance myInstance = null ;
    private MyInstance(a){};
    public static MyInstance getInstance(a){
        if(myInstance ==null) { synchronized (MyInstance.class){  if(myInstance ==null) { myInstance = new MyInstance();  }  }  }  return myInstance ;  } } Copy the code

Two questions: first, why the volatile keyword is used; Second, why use double check lock

** First problem: ** uses the volatile keyword to keep the variable visible to multiple threads and prevent the same variable from being created more than once. Reordering is also disabled.

** Second problem: ** uses a double-checked lock to prevent one thread from being suspended until null is detected, and another thread from creating the object wakes up and creates the object again. Therefore, the new instance is locked, and the judgment is made again after locking. However, instruction reordering is likely to occur, so add the volatile keyword to the variable to prevent instruction reordering.

2. CAS

What is CAS? Compare and replace: when assigning a value to an element, first check whether the element is the desired value, modify it if so, and return false if not


getAndAddIntSource code (spin +CAS)

//o1 is the object, v1 is the memory offset of the field, and v2 is the value to be incremented
public int getAndAddInt(Object o1 , long v1,int v2){
    int v3 ;
    do{
        // Get the value in the specified memory
 v3 = this.getIntVolatile(o1,v1);  // Check whether the current value is still the obtained value (place other thread changes)  }while(this.compareAndSwapInt(o1,v1,v3,v3+v2))  return v3 ; } Copy the code

2.1 atomic classes

Atomic classes are implemented by CAS+ spin. Atomic operations are performed by directly comparing them with values in memory.2.1

2.1 AtomicInteger

Underlying implementation: CAS operations and spins in the Unsafe class

** constructor: ** takes no argument, defaults to 0; Takes an argument and passes in a specified value

2.1 AtomicReference

Atomic reference, used to perform atomic operations on custom classes

AtomicReference<User> atomicReference = new AtomicReference<>();
        atomicReference.set(new User("1".1));
        System.out.println(atomicReference.get());
Copy the code

2.2 the CAS shortcomings

  • Long cycle time
  • ABAThe problem

3. ABA


That is, a thread performs a series of intermediate operations on a value in main memory, but the modification starts with the same result as the modification, causing other threads to assume that the value has not changed.

3.1 solve the ABA

It is implemented by adding a version number, similar to the implementation of optimistic locks in databases

A class AtomicStampedReference is provided in Java

User user01 = new User("1".1);
        User user02 = new User("2".2);
        // Initializes a value and a version number
        AtomicStampedReference<User> atomicStampedReference = new AtomicStampedReference<>(user01,100);
        boolean b = atomicStampedReference.compareAndSet(user02, user01, 100.101);
 boolean b1 = atomicStampedReference.compareAndSet(user01, user02, 101.101);  boolean b2 = atomicStampedReference.compareAndSet(user01, user02, 100.101);  System.out.println(b2); Copy the code

Compare twice, once for the value and once for the version number, and the ABA problem is solved

4. JUC three thread classes

4.1 CountDownLatch

The main thread does not start executing until all threads have finished executing.

CountDownLatch countDownLatch = new CountDownLatch(3);
        for(int i = 0 ; i < 3 ; i++){
            new Thread(()->{
                System.out.println("Output");
                countDownLatch.countDown();
 },"The thread is:"+i).start();   }  countDownLatch.await();  System.out.println("Main thread execution");  The outputThe outputThe outputMain thread executionCopy the code

** Principle implementation: ** When creating an object, passed in a need to wait the number of threads. When the thread completes the task by subtracting one by the countDown() method, and when this value is reduced to zero, the thread waiting with await() method is awakened and executed.

4.2 CyclicBarrier

When the specified number of threads are ready, the code is executed

CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
        for(int i = 0 ; i < 5 ; i++){
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"\ t enter");
                try{
 TimeUnit.SECONDS.sleep(1); }catch(InterruptedException e) {e.printStackTrace(); } try {  cyclicBarrier.await();  } catch (InterruptedException e) {  e.printStackTrace();  } catch (BrokenBarrierException e) {  e.printStackTrace();  }  System.out.println(Thread.currentThread().getName()+"\t Execution completed");  },"The thread is:"+i).start();  }  / * *  The thread is 1Thread: 0 to enterThe thread is: 2The thread is as follows: 3The thread is 4Thread: 0 the execution is completeThe thread is as follows: 2 The execution is completeThe thread is as follows: 4 The execution is complete The thread is blocked, waiting for another thread to enter before it can execute the task * / Copy the code

The ** constructor passes in a number of threads to indicate that the number of threads is needed before the code can be executed. If there are not enough threads, the thread blocks and waits

4.3 a Semaphore

Limiting the flow of threads

Semaphore semaphore = new Semaphore(3);
        for(int i = 0 ; i < 5 ; i++){
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"\t into execution");
                try {
 semaphore.acquire();  System.out.println(Thread.currentThread().getName()+"\ t leave");  } catch (Exception e) {  e.printStackTrace();  }finally {  semaphore.release();  }  },"The thread is:"+i).start();  } / / output The thread is:0Enter onThe thread is:2Enter onThe thread is:0leaveThe thread is:1Enter onThe thread is:1leaveThe thread is:2leaveThe thread is:3Enter onThe thread is:3leaveThe thread is:4Enter onThe thread is:4leaveCopy the code

5. Callable

Another way to create threaded tasks

5.1 Differences with Runnable

Runnable Callable
The return value There is no There are
An exception is thrown There is no There are
Execution method run cal

5.2 perform

  • What we found was thatThreadThere is no constructor passed directly to this interface in the class

  • By passing inRunnbaleThe implementation class implementation is passed inCallableinterface

Thread thread = new Thread(new FutureTask<Integer>(()->{return 1; }));Copy the code

6. Java troubleshooting

  • jps -aPrints the Java program being executed
  • Jstack ID no.To query the exact number of lines that failed

7. Thread status

  • NEWStart creating a thread
  • RUNNABLEThe thread enters the ready state
  • BLOCKEDThe thread is blocked
  • WAITINGThread waiting
  • TIMED_WAITINGThread timeout wait
  • TERMINATEDThread to destroy

Previous recommendations:

This article takes you through the architectural thinking of Spring MVC

Mybatis you only know CRUD

You know the IOC structure

This article is formatted using MDNICE