preface

Recently, the author has been reading the book Concurrent Programming in Java, which has a lot to say about high concurrency. Small white thinks more important part to make according to individual understanding with record! There will be many inaccuracies in the article, please understand and discuss together in the comment area!

What is multithreading?

To understand multithreading, you must first understand processes! Process originated from the concept of operating system, it is a computer program on a data set of a run of activities, is the system for resource allocation and scheduling of the basic unit. Threads can be thought of as lightweight threads, but unlike processes, the resources used by threads originate from processes.

Therefore, the process can be said to be a container of threads, a single process will contain one or more threads, multiple threads are allocated on the process resources after the execution of the corresponding instructions, and finally end its evil life!

Multithreaded state twist?

Thread state indicates whether a thread has been created, can be executed, or has been executed.

When the process knows what state the current thread is in, it knows whether to allocate memory resources to the thread, whether to allocate CPU resources to the thread, whether to reclaim all resources allocated from the thread, and so on.

It is to solve the above problems, reasonable definition of thread state and reasonable twist thread state, become the core problem of design thread!

The state and torsion process of the thread are shown below:

Thread state transitions are not closed loops. Threads that are in TERMINATED state will no longer return to the NEW state.

The state of multithreading is basically clear, and the official JDK team is so professional that they must also define a thread state enumeration for multithreading:

public class Thread implements Runnable{ //... public enum State NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED; }}Copy the code

Thread status description:

  • NEW: indicates the status of a thread that has been created but executed.
  • RUNNABLE: indicates the state of a thread that can be executed. There are two states at this point: one is that the thread is executing in the JVM, and the other is that the thread is waiting for other resources (CPU time slices) in the operating system.
  • BLOCKED: indicates the blocked state of the thread. The thread continues to suspend execution until the lock is acquired.
  • WAITING: indicates that the thread is in the infinite wait state. Unless the thread is awakened, the thread is always in the infinite wait state.
  • TIME_WAITING: indicates that the thread is in a finite state. Unlike wireless wait, the condition to continue execution in this state includes a timeout in addition to being awakened.

Now that we have a basic understanding of thread state, the threading API must be used to manipulate threads from one state to another.

Common thread operations and state transitions are shown in the following table:

Thread operation Initial state The end state
Thread.start NEW RUNNABLE
Thread.stop RUNNABLE TERMINATED
Thread.wait RUNNABLE WAITING
Thread.notify WAITING/TIME_WAITING RUNNABLE
Thread.wait(timeout) RUNNABL TIME_WAITING

How many ways are there to create threads?

If you are asked this question in an interview, you will answer one of two things: implement the Runnable interface and inherit the Thread class. The author small white also often is such answer, so so far still here evil complement multithreaded knowledge!

There are three ways to create a thread, other than by arranging a thread pool:

  • Implement the Runnable interface
  • Thread class inheritance
  • Implements the Callable interface

The Runnable interface and the Callable interface can be grouped together, while the Thread class inheritance method belongs to another dependency.

Thread class inheritance

public class MyThread extends Thread { @Override public void run() { //... } public static void main(String[] args) { MyThread thread = new MyThread(); // Start multithreading and change the thread state to Runnable thread.start(); }}Copy the code

The RUNNABLE Thread gets the CPU time slice and executes the code under the run() method, so it needs to inherit from Thread and override the run() method to do its job. In addition, executing as follows does not open multithreading, but merely calls the run() method in the current thread!

   public static void main(String[] args) {
         MyThread thread = new MyThread();
         thread.run();
     }
Copy the code

Implement the Runnable interface

public class MyRunnable implements Runnable { @Override public void run() { //... } public static void main(String[] args) {MyRunnable runnable = new MyRunnable(); Thread = new Thread(runnable); Thread.start (); }}Copy the code

“A class that implements Runnable can only be treated as a task that can be performed in a Thread, not a Thread, and therefore needs to be called through Thread. Tasks are executed by threading!” —-cyc2018 technical interview basics

To better understand this, you have to look at the source code for Thread and Runnable:

Public interface Runnable {public abstract void run(); } public class Thread implements Runnable {//... private Runnable target; public synchronized void start() { if (threadStatus ! = 0) throw new IllegalThreadStateException(); group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (! started) { group.threadStartFailed(this); }} catch (Throwable ignore) {}}} private void start0(); @override public void run() {// Determine whether the private target is null if (target! = null) {// Call the target method target.run(); }}}Copy the code

The first thing He looks at is the thread.start () method, which calls the local method thread.start0 (). The local method thread.start0 () is used to request a Thread from the operating system and execute the instruction under thread.run () after the Thread gets the CPU time slice.

MyThread has two responsibilities. One is to apply for the operating system Thread resources, and the other is to define the tasks to be performed by the Thread.

Since this is a violation of the single responsibility principle, the official JDK team must not ignore DER. Therefore, you can see policy patterns in the Thread source code. You see the official or compared with the above brief source code, to determine whether the responsibilities are separated yo!

Therefore, it is quite a preparation for cycy2018 boss to say that Runnable implementation class is just task and Thread is Thread driven.

Implements the Callable interface

Callable interfaces Compared to Runnable interfaces, Callable interfaces can have return values, which are encapsulated by FutureTask.

public class MyCallable implements Callable<Integer> { @Override public Integer call() throws Exception { //... return 0; } public static void main(String[] args) throws ExecutionException, {// Task MyCallable callable = new MyCallable(); InterruptedException {// Task MyCallable callable = new MyCallable(); // Task adapter FutureTask<Integer> FutureTask = new FutureTask<>(callable); Thread = new Thread(futureTask); thread.start(); Integer res = futureTask.get(); }}Copy the code

The Callable implementation class is a task with a return value, while Thread is still equivalent to a Thread driver, and the FutureTask class is more of an adapter. Because the attribute Target in the Thread class must be a Runnable class or subclass, FutureTask implements the Runnable interface. What we want to execute is call() in the Callable class and have a return value, so FutureTask includes the Callable instance and overwrites the run() method to call().

Public interface Callable<V> {V Call () throws Exception; Public class FutureTask<V> implements RunnableFuture<V> {//... private Callable<V> callable; public void run() { if (state ! = NEW || ! UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c ! = null && state == NEW) { V result; boolean ran; Try {// call() method result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { runner = null; int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); }}}Copy the code

summary

As for how to create multithreading, this section has recorded so much for the time being. To the question of which way to implement the interface or inherit the Thread, “Java High Concurrent Programming” answered interface! The authors argue that Java only supports single inheritance, so inheritance is a valuable resource, and why waste resources when implementing interfaces can do the same thing! In my opinion, the JDK official team recommends that we use it as it should.

How do I stop a thread?

Stopping a thread not only changes the thread from the RUNNABLE state to the TERMINALED state, but also includes issues such as how the terminated thread releases the locks it holds.

Thread. Stop method?

The old JDK provided apithread.stop () for stopping threads, but this method has been deprecated. Why did they abandon them?

Examples from the book illustrate the problem:

Public class StopThread {// simulate User public static stopthread.user u = new User(); Public static void main(String[] args) throws InterruptedException {// Enable the read thread. New ReadObjectThread().start(); While (true) {Thread t = new ChangeObjectThread(); t.start(); Thread.sleep(150); t.stop(); Public static class User {private int id; public static class User {private int id; private String name; Public static class ChangeObjectThread extends Thread {@override public void run() { While (true) {synchronized (u) {synchronized int v = (int) (system.currentTimemillis () / 1000); // Change ID U. SCetid (v); Try {// the current Thread sleeps 100ms thread.sleep (100); } catch (InterruptedException e) { e.printStackTrace(); } // Change name U. scetname (string.valueof (v)); } // The current Thread's CPU time slice is surrendered, and the running Thread is converted to the ready state, and the competing CPU scheduling thread.yield (); Public static class ReadObjectThread extends Thread {@override public void run() {while (true) { Synchronized (u) {synchronized (u) {synchronized (u) {if (u.getid ()! = Integer.valueOf(u.getName())) { System.out.println(u.toString()); }} // Yield (); }}}}Copy the code

The main function in this example: the writer thread changes its ID and name to the same value each time, so there should be no output in normal logic!

The actual result of execution is:

 User{id=1627139301, name='1627139300'}
Copy the code

There is a data inconsistency problem when the lock is added.

The problem lies in the thread.stop () method, which releases all locked resources held by the Thread, resulting in data inconsistency.

How do I design a secure thread.stop () method?

In fact, the main consideration of “security” is how to release the lock resources. The small white thinks that as long as the thread executes the synchronization block to release the lock and then terminate the thread is OK

Public class DiyStopThread {// Simulate User public static diyStopthread.user u = new diyStopthread.user (); Public static void main(String[] args) throws InterruptedException {// Enable the read thread new DiyStopThread.ReadObjectThread().start(); while (true) { ChangeObjectThread t = new DiyStopThread.ChangeObjectThread(); t.start(); Thread.sleep(150); // Stop the thread. TopMe (); Public static class ChangeObjectThread extends Thread {private volatile Boolean stopMe =. Public static class ChangeObjectThread extends Thread {private volatile Boolean stopMe = false; Public void stopMe() {stopMe = true; } @override public void run() {while (true) {if (stopMe) {system.out.println ("Thread exit."); break; } synchronized (u) { int v = (int) (System.currentTimeMillis() / 1000); // Change ID U. SCetid (v); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } // Change name U. scetname (string.valueof (v)); } // Remove the CPU time slice from the current Thread, and the running Thread will return to the ready state and recompete the CPU scheduler thread.yield (); }}} // Static inner class UserCopy the code

The custom writer thread determines whether to terminate the loop by the flag bit. When the flag bit is set, it breaks out of the loop and releases the lock resource to terminate the thread. Strictly ensure that the lock resource is released and then exit the thread, simple and efficient to avoid data inconsistency!

The JDK’s interrupt mechanism

This Part will talk about how to terminate threads. How to interrupt? But before answering this question, we need to consider the following question: what is the state of the interrupted thread?

Presumably the answer is either TERMINALED or WAITING. So what’s the real result?

“There is no linguistic requirement that an interrupted program should terminate. Interrupt a thread only to get that thread’s attention, and the interrupted thread can decide what to do with the interrupt “—– Java core volume

TERMINALED/WAITING/TIME_WAITING/BOCKED TERMINALED/WAITING/TIME_WAITING/BOCKED

Therefore, we can again use the interrupt mechanism to achieve a safe way to terminate the thread. Before we use it again, let’s introduce the API related to interrupt mechanism:

  • public void Thread.interrupt(): Interrupts the thread.
  • public boolean Thread.isInterrupted(): Determines whether the thread is interrupted.
  • public static boolean Thread.interrupted(): Determines whether the thread is interrupted and clears the interrupted status.

In fact, the use of interrupt mechanism to end the thread and the above method of custom safe end thread is not much different, both using flag bit implementation.

Public static interruptstopthread.user u = new Interruptstopthread.user (); Public static void main(String[] args) throws InterruptedException {// Create the write thread new InterruptStopThread.ReadObjectThread().start(); ChangeObjectThread = new ChangeObjectThread(); ChangeObjectThread = new ChangeObjectThread(); changeObjectThread.start(); Thread.sleep(200); changeObjectThread.interrupt(); }} public static class ChangeObjectThread extends Thread {@override public void run() {while (true) { If (thread.interrupted ()) {system.out.println ("Thread exit."); break; } synchronized (u) { int v = (int) (System.currentTimeMillis() / 1000); // Change ID U. SCetid (v); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } // Change name U. scetname (string.valueof (v)); } // Remove the CPU time slice from the current Thread, and the running Thread will return to the ready state and recompete the CPU scheduler thread.yield (); }}} // Static inner class UserCopy the code

The actual execution will throw InterruptException, but don’t jump to conclusions. This formal interruption mechanism is different from the advantages of custom flag bits!

If the thread is in the sleeping (TIME_WAITING) state, the custom flag bit mode must wait until the thread is in the sleeping (TIME_WAITING) state to become RUNNABLE before terminating the thread, but there is nothing to do with the thread in the WAITING/TIME_WAITING state. This is where custom flag bits interrupt threads, but the interrupt mechanism is different. An InnterruptException is thrown when a thread is in the WAITING/TIME_WAITIN state and is in the WAITING/TIME_WAITIN state. And custom flag bit way, not a bit advanced, right!

The authors also provide a good example of how interrupts can be used:

public static void SleepThreadInterrupt() throws InterruptedException { Thread t1 = new Thread() { @Override public void Run () {while (true) {// determine if the thread is interrupted, If (thread.currentThread ().isinterrupted ()) {system.out.println ("Interrupted"); break; } try { Thread.sleep(2000); } catch (InterruptedException e) { System.out.println("Interrupted When Sleep"); // Clear the interrupt status thread.currentThread ().interrupt(); } Thread.yield(); }}}; // Start the thread t1.start(); Thread. Sleep (1000); // Interrupt thread t1.interrupt(); }Copy the code

Wait and wake up

WAIT and wake mainly involve the twist between the two thread states: Runnable and WAIT/TIME_WAITING.

Object. The wait () and the Object. Notify ()

The wait() and notify() methods in the Object class? The Object class does contain two methods:

Public class Object {//... public final void wait() throws InterruptedException { //... } public final native void notify(); }Copy the code

“When the wait() method is called on an object instance, the thread will wait on the object until another thread calls the object’s notify() method.” Naturally, thread wait and thread wake cannot necessarily occur in a unified thread, which means that there are object instances as the medium for thread wait and wake. Then the official JDK team decided that any instance of an Object could be this medium, so the parent class Object of all classes naturally includes these two methods.

But how exactly do the wait() and notify() methods work?

The first idea of minibus is to find the answer in the Object source code:

public class Object { public final void wait() throws InterruptedException { wait(0); Public final native void wait(long timeout) throws InterruptedException; Public final native void notify(); }Copy the code

However goose source call the local method, the method corresponds to the JDK C++ source code, small white here ability limited will not be introduced! (To be added later)

The author of “Java Highly Concurrent Programming” also explains in the book, just see how the author explains:

When a thread calls object.wait(), it enters the wait queue for an object.

When another thread calls object.notify(), a randomly selected thread from the queue will wake up. Note: Random does not mean fair, as it is not the queue that waits first that wakes up first.

In addition, the Object class contains the notify() method, which, when called, wakes up all waiting threads in the waiting queue.

Note: the object.wait ()/ object.notify ()/ object.notifyAll () methods need to be called inside a synchronized block, that is, in a synchronzied statement.

Here is a simple example of how to use wait() and notify() :

public class SimpleWaitNotify { final static Object object = new Object(); Public static class T1 extends Thread {@override public void run() {synchronized (object) {synchronized Object.wait (); } catch (InterruptedException e) { e.printStackTrace(); }}}} public static class T2 extends Thread {@override public void run() {synchronized (object) {// Wake up a Thread in the object queue  object.notify(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { Thread t1 = new T1(); Thread t2 = new T2(); t1.start(); t2.start(); } // Run 1627226573605:T1 start! 1627226573606:T1 wait for object 1627226573606:T2 start! Notify One Thread 1627226573606:T2 end! 1627226575613:T1 end!Copy the code

The thread.wait () method with no arguments has been demonstrated above, but there are actually overloaded methods of thread.wait () that include a timeout. Thread.wait(long timeout), like thread.sleep (), makes the Thread wait a certain number of times. Unlike sleep(), wait(long timeout) releases the lock on the target object.

So let’s comb through the execution of the above example:

After the T1 thread is enabled, the T1 thread obtains the lock object and executes the synchronized code block. Note: when executing the object.wait() statement, the thread first obtains the object monitor, then moves from the RUNNABLE state to WAITING state, finally releases the object monitor and releases the lock object. The next step is the execution of the T2 thread, which will not be repeated:

Object monitors can be simply understood as object queues, but the actual object monitors are not really just queues. For the normal monitor this part of the knowledge, the small white is not very understanding of the future and then gradually in-depth!

Suspend and continue execution

The functions of the thread.suspend () and thread.resume () apis, which suspend and resume threads, respectively, seem to bring convenience to Thread control like thread.stop (), but are actually deprecated like the thread.stop () methods. The core problem is that a suspended thread does not release any locks. We know that if we don’t release thread-locked resources, we can easily cause deadlock problems, which adds to the burden of multi-threaded development.

In addition, when the resume() operation is executed prior to suspend() due to a reorder of instructions, the suspended thread will almost never execute again and will not release the locks it holds, easily leading to a deadlocked state. More importantly, when you look at the state of a thread using JStack, it turns out that the thread is in a RUNNABLE state.

public class BadSuspend { public static Object u = new Object(); static ChangeObjectThread t1 = new ChangeObjectThread("t1"); static ChangeObjectThread t2 = new ChangeObjectThread("t2"); public static class ChangeObjectThread extends Thread { public ChangeObjectThread(String name) { super.setName(name); } @Override public void run() { synchronized (u) { System.out.println(" in" + getName()); CurrentThread ().suspend(); // Suspend Thread thread.currentThread ().suspend(); System.out.println(); } } } public static void main(String[] args) throws InterruptedException { t1.start(); Thread.sleep(1000); t2.start(); t1.resume(); // The t2.resume() method is executed before the T2 thread is interrupted. t1.join(); t2.join(); }}Copy the code

View Java process PID and thread status using JPS/JStack:

 D:\WSharkCoder\WorkSpace\IdeaProjects\DeepLearn-Concurrent>jps
 12528 BadSuspend
 12036 Jps
 15716 Launcher
 2548
 D:\WSharkCoder\WorkSpace\IdeaProjects\DeepLearn-Concurrent>jstack -l 12528
 2021-08-08 15:32:46
 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.281-b09 mixed mode):
 ​
 "t2" #13 prio=5 os_prio=0 tid=0x000001d934518000 nid=0x2468 runnable [0x00000089e9eff000]
    java.lang.Thread.State: RUNNABLE
         at java.lang.Thread.suspend0(Native Method)
         at java.lang.Thread.suspend(Thread.java:1032)
         at base.suspendresume.BadSuspend$ChangeObjectThread.run(BadSuspend.java:21)
         - locked <0x000000076b99d138> (a java.lang.Object)
 ​
Copy the code

Imagine a project in which a large number of threads are executing simultaneously. It could take three or five days to fix this problem with thread messages like this!

So how do you implement a safe thread suspend and continue execution? The wait and wake API allows you to suspend and continue execution. The code implementation will not be repeated here. The operation is basically the same as the wait and wake API.

Collaboration/humility

Thread.join()

Collaboration is one of the most common cooperative behaviors in everyday human life! Multithreading itself is to enhance the processing of certain tasks, so there must be some threads must wait for other threads to complete their results before processing data. So, there must be inter-thread and collaboration.

Common apis for thread collaboration have:

  • public final void join() throw InterrupedExceptionThe thread waits indefinitely until the target thread finishes executing
  • public final void join(long millis) throw InterrupedExceptionThe thread waits until the maximum millis, and the target thread can continue execution either after completion or beyond the maximum wait time

Here is an example of using the child thread to compute a value and the parent thread to wait until the result set is finished:

public class JoinThread { public volatile static int i = 0; public static class AddThread extends Thread { @Override public void run() { int target = 1000000000; while (i < target) { i++; } } } public static void main(String[] args) throws InterruptedException { Thread t = new AddThread(); t.start(); // Wait for the thread to finish executing before printing the result. System.out.println(i); }}Copy the code

Thread.yeild()

If you’ve seen this API many times before, what does yeild() do?

The main function of yeild() is to give up the CPU and compete with other threads again for CPU resources. This API can be called when a thread’s main task has completed, giving other threads a higher probability of execution.

Note: Other threads may not execute immediately, and it is usually random which thread is executed.

Github Repository: github.com/WSharkCoder…

conclusion

There is a lot of knowledge about multithreading, and the only part recorded here is the thread state torsion and the thread API. In fact, the author also talked about volatile and JMM, sychronized, thread groups and the set framework under threads in this chapter. This part of the knowledge needs to be more detailed dig, and it is estimated that it is about the same length as this article, so I will add more knowledge later. If you think white shark’s blog is helpful to you, welcome to like the collection comments!

reference

High Concurrency Programming for Java

Java thread synchronization mechanism