Whether you’re a freshman in college or a CRUD with 2-3 years of experience, that means you’ll be asked these questions for at least 3 years. Take the time to get to the bottom of them. The best way to get rid of your fear is to face him, Ollie! This series is my notes and summary of the learning process, and provides debugging code for everyone to play with

The chapter reviews

1. What are the differences between interrupt, isInterrupted and interrupted?

2. What operations do begin and end perform?

3. Where is the private volatile Interruptible blocker parameter set?

Please review the above questions by yourself, or the previous chapter if you have any questions

In this chapter the feed

By the end of this chapter, you will have a systematic knowledge of synchronized and be able to use it well to solve thread-safety problems. (As always, students who are familiar with this section can choose to direct focus on 👍 to complete this chapter!)

This chapter code download

What is thread safety

Thread safety problem refers to the problem of data synchronization caused by shared resources between multiple threads when these shared resources are invoked simultaneously. To get a clearer picture of thread-safety issues, let’s take a look at several thread-unsafe phenomena using a code with thread-safety issues.

Go straight to code

Public class UnSafeThreadExample implements Runnable {private int sum = 1; public class UnSafeThreadExample implements Runnable; private final static int MAX = 500; @Override public voidrun() {
    while (sum <= MAX) {
      System.out.println("Currently there are :" + (sum++) + "People like it.");
      try {
        Thread.sleep(100);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
  
  public static void main(String[] args) {
    final UnSafeThreadExample unSafeThreadExample = new UnSafeThreadExample();
    for(int i = 0; i < 5; i++) { new Thread(unSafeThreadExample).start(); }}}Copy the code

Several times we can generalize the following types of problems:

1. Repeat likes

We are not in a hurry to think about how to solve the problem, the premise of solving the problem is to understand how the problem is produced, so as not to treat the symptoms of plastic engineering.

Sum =486; sum=486; sum=486; sum=486

2. Thread A does sum+1 in its workspace, but for some reason the CPU passes to thread B

3. Thread B found that sum=486 was fetched from main memory because there was no sum in its local workspace

Sum =487; sum=487; sum=487; sum=487; sum=487;

5. Thread B refreshes sum to main memory, where sum=487. At the same time, thread B finishes the console output, and the whole thread life cycle ends.

6. Thread B releases the CPU execution authority. Then thread A obtains the CPU execution authority, and thread A continues to complete the previous task

7. Thread A accumulates and assigns, sum=487, while thread B completes the console output.

invisibility
486

2. The number of likes exceeds the upper limit

2. Thread B pauses for some reason (thread switching, thread blocking, thread sleep, etc.)

3. Sum =500 is flushed to main memory and output to the console

4. At this time, thread B acquires CPU execution right and starts execution again. At this time, thread B needs to obtain sum=500 from main memory again because sleep finds that sum=400 has expired

5. Thread B runs sum=501 to main memory and outputs sum=501 to the console


The two thread unsafe scenarios listed above are generally caused by shared variables to invisibility and operations to non-atomicity between multiple threads. Here is a way to solve these problems synchronized keyword.

Synchronized keyword

For this keyword analysis of our old method is obviously not applicable, here to teach you a trick official documents. JDK 8 official documentation

This document is updated every time the JDK is updated, and we are referring to the widely used JDK version 8.

1. How to understand this keyword

Synchronized is described in detail in the official documentation, and you are advised to read section 17.1 carefully. Here because of the length here will not take you a sentence to product, I give you a summary:

1. Synchronized provides a locking mechanism, mainly targeting at the monitoring lock, which is an exclusive locking mechanism to ensure the mutual exclusion of shared variables in the multithreaded programming process, so as to prevent data inconsistency.

Synchronized can be used on variables, methods, and classes, but cannot be used on null, otherwise a null pointer exception will be reported.

  public static void main(String[] args) {
    UnSafeThreadExample t = null;
    synchronized (t) {
      System.out.println("made it!"); }}Copy the code

3. Synchronized Can obtain the same monitoring lock multiple times. An example of multiple calls is also provided in the official documentation.

Example 14.19-1. The synchronized Statement class Test {public static void main(String[] args) {Test t = new Test(); synchronized (t) { synchronized (t) { System.out.println("made it!");
        }
      }
    }
  }
This program produces the output:
made it!
Copy the code

Of course, synchronized provides us with a safe solution, but there is never a perfect solution. There are advantages and disadvantages, and we will gradually show its disadvantages in the later use process.

2. Synchronized of actual combat

1. Revamp the likes process before

public class SafeThreadExample implements Runnable {

  private int sum = 1;
  private final static int MAX = 500;
  private final static Object MUTEX = new Object();

  @Override
  public void run() {
    synchronized (MUTEX) {
      while (sum <= MAX) {
        System.out.println("Currently there are :" + (sum++) + "People like it.");
        try {
          Thread.sleep(10);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
  }

  public static void main(String[] args) {
    final SafeThreadExample safeThreadExample = new SafeThreadExample();
    for(int i = 0; i < 5; i++) { new Thread(safeThreadExample).start(); }}}Copy the code

Here we create a MUTEX shared object and lock it internally with synchronized, so that only one thread in multiple threads can acquire the monitor lock of this object, so that there is no thread unsafe situation. But this is also a problem. When we run the program, we can find that the speed is greatly reduced. This is one of the drawbacks of using the synchronized keyword is that it can slow down our programs.

2. Use JConsole to check the status of each Synchronized thread

Still use the “like” program to open the operation, let’s first change the sleep time to 5 minutes so that we can observe

Then we enter the jConsole command on the console to open the JConsole interface.

sleep
TIMED_WAITING
Status: BLOCKED on java.lang.Object@10eb0916, owner: Thread-0
The exclusivity of Synchronized

3. Disassemble javAP instructions to view underlying instructions

Javap doesn’t know if you’ve used this directive before, but I’ll briefly describe the process here.

1. Use JavAC to disassemble Java files to generate corresponding class files

javac /Users/doudou/workspace/6m/ThreadStudy/src/com/lyf/page4/SafeThreadExample.java
Copy the code

2. Go to the directory where the class file is generated

cd /Users/doudou/workspace/6m/ThreadStudy/src/com/lyf/page4
Copy the code

3. Disassemble class files using JavAP instructions

javap -c SafeThreadExample
Copy the code

We intercept the result of the run() section as follows:

 public void run();
    Code:
       0: getstatic     #4 // Field MUTEX:Ljava/lang/Object;
       3: dup
       4: astore_1
       5: monitorenter
       6: aload_0
       7: getfield      #3 // Field sum:Ljava/lang/Integer;
      10: invokevirtual #5 // Method java/lang/Integer.intValue:()I
      13: sipush        500
      16: if_icmpgt     92
      19: getstatic     #7 // Field java/lang/System.out:Ljava/io/PrintStream;
      22: new           #8 // class java/lang/StringBuilder
      25: dup
      26: invokespecial #9 // Method java/lang/StringBuilder."
      
       ":()V
      
      29: ldc           #10 // String already has:
      31: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;) Ljava/lang/StringBuilder;
      34: aload_0
      35: getfield      #3 // Field sum:Ljava/lang/Integer;
      38: astore_2
      39: aload_0
      40: aload_0
      41: getfield      #3 // Field sum:Ljava/lang/Integer;
      44: invokevirtual #5 // Method java/lang/Integer.intValue:()I
      47: iconst_1
      48: iadd
      49: invokestatic  #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      52: dup_x1
      53: putfield      #3 // Field sum:Ljava/lang/Integer;
      56: astore_3
      57: aload_2
      58: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;) Ljava/lang/StringBuilder;
      61: ldc           #13 // String people like
      63: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;) Ljava/lang/StringBuilder;
      66: invokevirtual #14 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      69: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;) V
      72: getstatic     #16 // Field java/util/concurrent/TimeUnit.MINUTES:Ljava/util/concurrent/TimeUnit;
      75: ldc2_w        #17 // long 5l
      78: invokevirtual #19 // Method java/util/concurrent/TimeUnit.sleep:(J)V
      81: goto          6
      84: astore_2
      85: aload_2
      86: invokevirtual #21 // Method java/lang/InterruptedException.printStackTrace:()V
      89: goto          6
      92: aload_1
      93: monitorexit
      94: goto          104
      97: astore        4
      99: aload_1
     100: monitorexit
     101: aload         4
     103: athrow
     104: return

Copy the code

Don’t panic when you see this, let’s take a look at what this paragraph is really about.

  • 0: getstatic #4

Gets a MUTEX object

  • 5: monitorenter

Execute the Monitorenter JVM instruction to obtain the MUTEX monitor lock

The middle paragraph is about reading sum and MAX, and then making a judgment and adding them up

  • 93: monitorexit

Execute the Monitorexit JVM directive to release the monitor lock on the MUTEX object

If you look carefully, you’ll notice that there are two MoniterExit directives. Line 100 is also a Moniterexit directive.

  • The first Monitorexit releases the lock after the program exits normally
  • The second Monitorexit releases the lock after an asynchronous exit occurs

monitorenter

Each object has its own monitor lock, and each monitor lock can only be acquired by one thread at a time. Each monitor has its own counter. Here are the points related to monitorenter

1. Only when the monitor counter is 0, it indicates that the monitor lock of this object has not been acquired, and then the thread can attempt to acquire the corresponding monitor lock. Once it succeeds, the counter will be accumulated.

2. Because of the reentrancy of Synchronized, re-entering a thread already possessed causes the Monitor counter to increment by one

monitorexit

According to the happen-before principle, each monitorexitJVM directive must be executed before the object is retrieved to the monitor lock. That is, each Monitorexit requires another Monitorenter. Monitorext decrement the Monitor counter by one.

4. A deadlock

Experience a deadlock scenario with code

Without further ado, a piece of code on the line

Public final static Object GIVE_THE_THUMBS_UP = new Object(); public final static Object GIVE_THE_THUMBS_UP = new Object(); Private final static Object GIVE_THE_THUMBS_OFF = new Object(); public voidup() {
    synchronized (GIVE_THE_THUMBS_UP) {
      synchronized (GIVE_THE_THUMBS_OFF) {
        System.out.println("I'm gonna give you a thumbs up.");
      }
      System.out.println("System prompt: Thumbs up successfully");
    }
  }


  public void off() {
    synchronized (GIVE_THE_THUMBS_OFF) {
      synchronized (GIVE_THE_THUMBS_UP) {
        System.out.println("I'm gonna unlike you.");
      }
      System.out.println("System prompt: Unlike success");
    }
  }

  public static void main(String[] args) {
    DeathLockExample deathLockExample = new DeathLockExample();

    new Thread(() -> {
      while (true) { deathLockExample.up(); }},"Like thread").start();

    new Thread(() -> {
      while (true) { deathLockExample.off(); }},"Cancel the like thread.").start(); }}Copy the code

The purpose of this code is to open two threads, one thread is responsible for liking, one thread is responsible for unliking, ha ha ha here is an example, we can not cancel the like ~ ~ 😁

When it runs, the output looks like this

The current thread does not terminate by itself at all

java.lang.Object@54e53d48
java.lang.Object@46b2e8cb
java.lang.Object@46b2e8cb

Another drawback of the Synchronized keyword is that it can lead to deadlocks. Such a deadlock is the classic philosopher’s noodle problem.

Further reading

Cause of deadlock

1. Cross-locks cause deadlocks

The program we wrote above is cross-locking, a deadlock situation caused by waiting for each other to release their own locks.

2. The memory is insufficient

Running out of memory during concurrent requests can also lead to deadlocks. This is the common situation where computers are stuck. Killing processes to release memory can solve some problems.

3. The lock cannot be released due to an unexpected program exit

For example, when you read or write a file, a thread acquires the write lock of the file, crashes and exits unexpectedly for some reason, but the write lock cannot be released at this time, which also causes a deadlock.

4. An infinite loop causes a deadlock

In principle, the beginning of an infinite loop does not cause a deadlock, but because an infinite loop keeps happening, CPU resources run out, and other threads do not get the CPU resources they should, it also causes the application to deadlock. However, this kind of deadlock is difficult to detect, one of its characteristics is high CPU.


C. synchronized use summary

1. Synchronized cannot apply to null objects and must be initialized before synchronization.

2. Synchronized will cause the thread to enter the blocking state, affecting the running state of the program under multi-threading. Therefore, the scope of synchronized cannot be too wide, and rational use is required.

3. Avoid cross-locks and deadlocks.


Well ~ this chapter has been completed, although synchronized is relatively heavy weight, but his exclusivity and reentrance in daily development application is very widespread oh, a lot of JDK source code is also applied to this keyword, students can also try to use it in daily development, enhance memory.

Finally study completed the classmate trouble move finger, little praise 👍 oh, I wish everyone good health, happy weekend ~ 😄