preface

Multithreaded concurrent programming is an important part of Java programming, but also the focus of the interview coverage area, so it is extremely important for us to learn multithreaded concurrent programming, let’s start this learning journey with me.

The body of the

Threads and processes

Thread is the sequential control flow in the program and can only use the resources and environment allocated to the program

2 Process: an executing program EACH process contains at least one thread

Single thread: There is only one thread in the program, and the main method is actually the main thread

Multithreading: Running multiple tasks in a program for better use of CPU resources

Implementation of threads

Thread class inheritance

As defined in the java.lang package, inheriting Thread classes must override the run() method

1 class MyThread extends Thread{ 2 private static int num = 0; 3 4 public MyThread(){ 5 num++; 6} 7 8 @override 9 public void run() {10 system.out.println (" +num+"); 12 11}}Copy the code

Once you have created your own thread class, you can create a thread object and start the thread with the start() method. Note that the run() method is not called to start the thread. The run method only defines the tasks that need to be executed. If you call the run method, you execute the run method on the main thread, just like a normal method call.

 

1 public class Test { 2 public static void main(String[] args) { 3 MyThread thread = new MyThread(); 4 thread.start(); 5 } 6 } 7 class MyThread extends Thread{ 8 private static int num = 0; 9 public MyThread(){ 10 num++; 11} 12 @override 13 public void run() {14 system.out.println (" +num+"); 16 15}}Copy the code

 

In the above code, a new thread is created by calling the start() method. To tell the difference between a start() method call and a run() method call, look at the following example:

 

1 public class Test { 2 public static void main(String[] args) { 3 System.out.println(" thread.currentThread () :"+ thread.currentThread ().getid ()); 4 MyThread thread1 = new MyThread("thread1"); 5 thread1.start(); 6 MyThread thread2 = new MyThread("thread2"); 7 thread2.run(); 8 } 9 } 10 11 class MyThread extends Thread{ 12 private String name; 13 14 public MyThread(String name){ 15 this.name = name; 16 } 17 18 @Override 19 public void run() { 20 System.out.println("name:"+name+" The child Thread ID: "+ Thread. CurrentThread () getId ()); 22 21}}Copy the code

 

Running results:

The following conclusions can be drawn from the output results:

Thread1 and thread2 have the same ID. Thread2 and thread2 have the same ID. Thread2 and thread2 have the same ID.

Thread1’s start method is called before thread2’s run method, but the first output is about thread2’s run method, indicating that the creation of a new thread does not block subsequent execution of the main thread.

Implement the Runnable interface

In addition to inheriting the Thread class, creating threads in Java provides similar functionality by implementing the Runnable interface. Implementing the Runnable interface must override its run method. Here’s an example:

1 public class Test {2 public static void main(String[] args) {3 system.out.println (" "+Thread.currentThread().getId()); 4 MyRunnable runnable = new MyRunnable(); 5 Thread thread = new Thread(runnable); 6 thread.start(); 7 } 8 } 9 class MyRunnable implements Runnable{ 10 public MyRunnable() { 11 } 12 13 @Override 14 public void run() { 15 System.out.println(" child Thread ID: "+ thread.currentThread ().getid ()); 17 16}}Copy the code

As the name implies, by implementing the Runnable interface, we define a subtask and assign the subtask to Thread. Note that this approach must take Runnable as an argument to the Thread class, and then create a new Thread to execute the subtask through the Thread start method. If you call Runnable’s run method, no new thread is created, just like a normal method call.

In fact, if you look at the implementation source code for the Thread class, you’ll see that the Thread class implements the Runnable interface.

In Java, either of these methods can be used to create threads to execute subtasks, depending on your needs. Inheriting Thread directly may seem cleaner than implementing the Runnable interface, but since Java only allows single inheritance, if a custom class needs to inherit from another class, it has to implement the Runnable interface.

Implement multithreading with returns using ExecutorService, Callable, and Future

We’ll learn more about multithreading later, but just for the moment, just to know that there’s a way to do it.

ExecutorService, Callable, and Future objects are all functional classes within the Executor framework. Threads that return results are a new feature introduced in JDK1.5 that is really useful because I don’t have to work too hard to get a return value, and even if I do, it can be buggy.

Tasks with returned values must implement the Callable interface, and similarly, tasks with no returned values must implement the Runnable interface. Call get on this Object to obtain the Object returned by the Callable task. With the help of the thread pool interface ExecutorService, you can implement multithreading with the result returned. The following provides a complete example of multi-threaded testing with returned results, which can be used directly in JDK1.5. The code is as follows:

4 public class Warning ("unchecked") 5 public class Warning {6 public static void main(String[] args) Throws ExecutionException, 7 InterruptedException {8 System.out.println("---- the program starts running ----"); 9 Date date1 = new Date(); 10 11 int taskSize = 5; 12 / / to create a thread pool 13 ExecutorService pool = Executors. NewFixedThreadPool (taskSize); 15 List<Future> List = new ArrayList<Future>(); 16 for (int i = 0; i < taskSize; i++) { 17 Callable c = new MyCallable(i + " "); 19 Future f = pool.submit(c); 20 // System.out.println(">>>" + f.get().toString()); 21 list.add(f); } 23 // disable thread pool 24 pool.shutdown(); 27 For (Future f: List) {29 System.out.println(">>>" + f.et ().toString()); 30 } 31 32 Date date2 = new Date(); 33 system.out. println("---- program end run ----, program run time [" 34 + (date2.getTime() -date1.getTime ()) + "ms] "); 35 } 36 } 37 38 class MyCallable implements Callable<Object> { 39 private String taskNum; 40 41 MyCallable(String taskNum) { 42 this.taskNum = taskNum; 43} 44 45 Public Object call() throws Exception {46 system.out.println (">>>" + taskNum + "task start "); 47 Date dateTmp1 = new Date(); 48 Thread.sleep(1000); 49 Date dateTmp2 = new Date(); 50 long time = dateTmp2.getTime() - dateTmp1.getTime(); 51 system.out.println (">>>" + taskNum + "); 52 return taskNum + "The current task time [" + time +" ms] "; 53}} 54Copy the code

The Executors class provides a series of factory methods for initiating the thread pool and returning thread pools that implement the ExecutorService interface. Public static ExecutorService newFixedThreadPool(int nThreads) Creates a thread pool with a fixed number of threads.

Public static ExecutorService newCachedThreadPool() creates a cacheable thread pool, and calls to execute reuse previously constructed threads (if available). If no existing thread is available, a new thread is created and added to the pool. Terminates and removes threads from the cache that have not been used for 60 seconds.

Public static ExecutorService newSingleThreadExecutor() creates a single-threaded Executor.

Public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) Creates a thread pool that supports timed and periodic task execution. It can be used instead of the Timer class in most cases.

ExecutoreService provides a submit() method that passes a Callable, or Runnable, and returns the Future. If the Executor background thread pool has not finished evaluating the Callable, this calls the get() method that returns the Future object and blocks until the evaluation is complete.

Thread state

Before we look at the methods in Thread, let’s take a look at the state of threads. This will help us understand the methods in Thread.

  • Create (new) state: A multithreaded object is ready
  • Runnable state: Calledstart()Method to wait for the CPU to schedule
  • Running state: Executionrun()methods
  • Blocked: Execution is temporarily halted, possibly freeing resources for use by other threads
  • Dead state: Thread destruction

When a new thread is needed to perform a subtask, a thread is created. However, once a thread is created, it does not immediately enter the ready state because it requires certain conditions (such as memory resources) to run. A thread enters the ready state only when all the conditions necessary to run are met.

When a thread enters the ready state, it does not get the CPU execution time immediately. Perhaps the CPU is doing something else at the moment, so it waits. When CPU execution time is given, the thread is actually running.

During the running state of the thread, there may be multiple reasons for the current thread not to continue running, such as the user actively let the thread sleep (sleep after a certain period of time), the user actively let the thread wait, or blocked by the synchronization block, which corresponds to multiple states: Time waiting (sleeping or waiting for an event), waiting (waiting to be awakened), and blocked.

When a thread dies due to a sudden interruption or completion of a subtask.

The following diagram depicts the state of a thread from creation to death:

In some tutorials, blocked, waiting, and time waiting are referred to as blocked states. This is ok, but here I want to associate the thread state with the method invocation in Java, so I separate waiting and time waiting states.

Note: What are the differences between sleep and wait?

  • sleepisThreadClass method,waitisObjectClass.
  • Thread.sleepDoes not cause the lock behavior to change if the current thread owns the lockThread.sleepThe thread will not release the lock.
  • Thread.sleepandObject.waitSuspends the current thread. The OS allocates execution time to other threads. The difference is, callwaitAfter that, another thread is required to executenotify/notifyAllTo regain the CPU execution time.

Context switch

For a single-core CPU, the CPU can only run one thread at a time. When running one thread switches to running another thread, this is called a thread context switch (similar for processes).

Because the task of the current thread may not complete, you need to save the running state of the thread during the switch so that the next time you switch back, you can continue to run in the previous state. Let’s take A simple example: let’s say thread A is reading A file and is halfway through the file. We need to pause thread A and switch to thread B. When we switch back to thread A, we don’t want thread A to read from the beginning of the file again.

So we need to record the running state of thread A, so what data is being recorded? Need to know when the next time the recovery before the current thread already where instruction execution, so you need to record the value of the program counter, such as other thread was a calculated suspended, so need to know before hanging up next time you’re continue when the value of a variable, so you need to record the state of the CPU registers. Therefore, generally speaking, data such as program counters, CPU register status, and so on are logged during thread context switches.

To put it simply: context switching for threads is really a process of storing and restoring CPU state, enabling thread execution to resume execution from a breakpoint.

Although multithreading can improve the efficiency of task execution, it will also bring some overhead costs when switching threads, and multiple threads will lead to an increase in the occupation of system resources, so we should pay attention to these factors in multithreading programming.

Common methods of threading

Serial number methods instructions
1 public void start() Causes the thread to start executing; The Java virtual machine calls the run method of this thread.
2 public void run() If the thread is constructed using a separate Runnable run object, the Runnable object’s run method is called. Otherwise, the method does nothing and returns.
3 public final void setName(String name) Change the thread name to be the same as the parameter name.
4 public final void setPriority(int priority) Changes the priority of a thread.
5 public final void setDaemon(boolean on) Mark this thread as a daemon thread or a user thread.
6 public final void join(long millisec) The maximum time to wait for this thread to terminate is Millis milliseconds.
7 public void interrupt() Interrupt the thread.
8 public final boolean isAlive() Tests whether the thread is active.
9 public static void yield() Suspends the currently executing thread object and executes the other threads.
10 public static void sleep(long millisec) Hibernates (suspends execution) the currently executing thread for a specified number of milliseconds, subject to the precision and accuracy of the system timer and scheduler.
11 public static Thread currentThread() Returns a reference to the thread object currently executing.

A static method

CurrentThread () method

The currentThread() method returns information about which thread the code snippet is being called by.

1 public class Run1{ 2 public static void main(String[] args){ 3 System.out.println(Thread.currentThread().getName()); 4} 5}Copy the code

Sleep () method

The method sleep() is used to put the current “executing thread” to sleep(pause execution) for a specified number of milliseconds. The “executing thread” is the thread returned by this.currentThread().

There are two overloaded versions of the sleep method:

2 sleep(long millis,int nanoseconds) // The first parameter is millisecond and the second parameter is nanosecondCopy the code

Sleep is the equivalent of letting the thread sleep, handing over the CPU, and letting the CPU perform other tasks. However, it is important to note that the sleep method does not release the lock, which means that if the current thread holds the lock on an object, no other thread can access the object even if the sleep method is called. Take a look at this example:

1 public class Test { 2 3 private int i = 10; 4 private Object object = new Object(); 5 6 public static void main(String[] args) throws IOException { 7 Test test = new Test(); 8 MyThread thread1 = test.new MyThread(); 9 MyThread thread2 = test.new MyThread(); 10 thread1.start(); 11 thread2.start(); 12 } 13 14 class MyThread extends Thread{ 15 @Override 16 public void run() { 17 synchronized (object) { 18 i++; 19 System.out.println("i:"+i); Println (" Thread "+ thread.currentThread ().getName()+" sleep "); 22 Thread.currentThread().sleep(10000); 23 } catch (InterruptedException e) { 24 // TODO: Handle Exception 25} 26 system.out.println (" Thread "+ thread.currentThread ().getName()+" sleep finished "); 27 i++; 28 System.out.println("i:"+i); 29} 30} 31} 32}Copy the code

Output result:

As can be seen from the above output results, When Thread0 entered the sleep state, Thread-1 did not perform specific tasks. Thread-1 can be executed only after thread0 has finished executing and the object lock is released by thread0.

Note that if the sleep method is called, you must catch InterruptedException or throw it to the upper layer. When a thread is full of sleep, it may not be executed immediately because the CPU may be working on other tasks. So calling sleep is blocking the thread.

The yield () method

Calling the yield method causes the current thread to surrender CPU permissions, allowing the CPU to execute other threads. It is similar to the sleep method in that it does not release the lock. However, yield does not control the exact time at which the CPU is surrendered. In addition, yield only allows threads with the same priority to obtain CPU execution time.

Note that, unlike the sleep method, invoking yield does not put the thread into a blocked state. Instead, it puts the thread back in a ready state, waiting only for CPU execution time to be retrieved. Code:

1 public class MyThread extends Thread{ 2 @Override 3 public void run() { 4 long beginTime=System.currentTimeMillis(); 5 int count=0; 6 for (int i=0; i<50000000; i++){ 7 count=count+(i+1); 8 //Thread.yield(); 9 } 10 long endTime=System.currentTimeMillis(); 11 system.out. println(" time: "+(endTime-beginTime)+" milliseconds!" ); 12 } 13 } 14 15 public class Run { 16 public static void main(String[] args) { 17 MyThread t= new MyThread(); 18 t.start(); 19}} 20Copy the code

Execution Result:

Time: 3 milliseconds!Copy the code

If the / / Thread. The yield (); The result is as follows:

Time: 16,080 milliseconds!Copy the code

Object methods

Start () method

Start () is used to start a thread. When the start method is called, the system starts a new thread to execute a user-defined subtask, allocating resources to the corresponding thread in the process.

The run () method

The run() method does not need to be invoked by the user. When a thread is started with the start method, it enters the body of the run method to perform specific tasks when it has acquired CPU execution time. Note that the Thread class must override the run method to define the specific tasks to be performed.

getId()

GetId () gets a unique identifier for the thread:

1 public class Test { 2 public static void main(String[] args) { 3 Thread t= Thread.currentThread(); 4 System.out.println(t.getName()+" "+t.getId()); 6 5}}Copy the code

Output:

main 1
Copy the code

IsAlive () method

The isAlive() method checks whether the current thread is active.

1 public class MyThread extends Thread{ 2 @Override 3 public void run() { 4 System.out.println("run="+this.isAlive()); 5 } 6 } 7 public class RunTest { 8 public static void main(String[] args) throws InterruptedException { 9 MyThread myThread=new MyThread(); 10 System.out.println("begin =="+myThread.isAlive()); 11 myThread.start(); 12 System.out.println("end =="+myThread.isAlive()); 14 13}}Copy the code

Program running results:

begin ==false
run=true
end ==false
Copy the code

The isAlive() method tests whether a thread is occasionally active. What is an active state? The active state is when the thread is started and not terminated. A thread is considered alive if it is running or ready to start running. There is one caveat

System.out.println("end =="+myThread.isAlive());
Copy the code

Although the value printed in the above example is true, this value is indeterminate. The value true is printed because the myThread thread has not finished executing, so it prints true. If the code is changed to the following, add a sleep:

1 public static void main(String[] args) throws InterruptedException { 2 MyThread myThread=new MyThread(); 3 System.out.println("begin =="+myThread.isAlive()); 4 myThread.start(); 5 Thread.sleep(1000); 6 System.out.println("end =="+myThread.isAlive()); 7}Copy the code

The output from the above code run is false because the MyThread object has completed execution in less than 1 second.

The join () method

In many cases, the main thread creates and starts the thread, and if the child thread does a lot of time-consuming computation, the main thread will often end before the child thread ends. In this case, if the main thread wants to wait until the child thread completes, for example, processing data, the main thread uses join() to retrieve the value in the data. The join() method waits for the thread object to be destroyed.

1 public class Thread4 extends Thread{ 2 public Thread4(String name) { 3 super(name); 4 } 5 public void run() { 6 for (int i = 0; i < 5; i++) { 7 System.out.println(getName() + " " + i); 8} 9} 10 public static void main(String[] args) throws InterruptedException {11 // Start child process 12 new Thread4("new thread").start(); 13 for (int i = 0; i < 10; i++) { 14 if (i == 5) { 15 Thread4 th = new Thread4("joined thread"); 16 th.start(); 17 th.join(); 18 } 19 System.out.println(Thread.currentThread().getName() + " " + i); 20} 21} 22}Copy the code

Execution Result:

1 main 0 2 main 1 3 main 2 4 main 3 5 main 4 6 new thread 0 7 new thread 1 8 new thread 2 9 new thread 3 10 new thread 4  11 joined thread 0 12 joined thread 1 13 joined thread 2 14 joined thread 3 15 joined thread 4 16 main 5 17 main 6 18 main 7 19 main 8 20 main 9Copy the code

As shown above, the main thread waits for the joined Thread to finish. If you comment out the th.join() line, the result is as follows:

1 main 0 2 main 1 3 main 2 4 main 3 5 main 4 6 main 5 7 main 6 8 main 7 9 main 8 10 main 9 11 new thread 0 12 new thread  1 13 new thread 2 14 new thread 3 15 new thread 4 16 joined thread 0 17 joined thread 1 18 joined thread 2 19 joined thread 3 20 joined thread 4Copy the code

GetName and elegantly-named setName

Used to get or set the thread name.

GetPriority and setPriority

Used to get and set thread priority.

SetDaemon and isDaemon

Used to set whether a thread is a daemon thread and determine whether a thread is a daemon thread.

The difference between a daemon thread and a user thread is that the daemon thread is dependent on the thread that created it, while the user thread is not. A simple example: if you create a daemon thread in the main thread, the daemon will die when the main method is finished running. The user thread does not. The user thread runs until it finishes running. In the JVM, threads like the garbage collector are daemon threads.

Now that I’ve covered most of the methods in the Thread class, what happens to Thread state when a method call is made? The following image is an improvement on the above image:

Stop the thread

Stopping thread is a very important technical point in the development of multi-thread. Mastering this technology can effectively deal with the stopping of thread. The thread.stop () method can be used to stop a Thread, but it is best not to use it. This method is unsafe and has been deprecated. There are three ways to terminate a running thread in Java:

  • Use exit flags to allow the thread to exit normally, that is, to terminate when the run method completes
  • Using the stop method to forcibly terminate a thread is not recommended because stop, like suspend and resume, are obsolete methods and may produce unexpected results.
  • Interrupt is used to interrupt a thread, but this does not terminate a running thread. A judgment is required to terminate the thread.

To suspend a thread

Interrupt () method

Priority of the thread

In the operating system, threads can be prioritized. Threads with higher priorities receive more CPU resources, that is, the CPU has the priority to perform tasks in the thread object with higher priorities. Setting a thread priority helps the thread planner determine which thread is selected for priority execution next time. To set the priority of a thread, use the setPriority() method, as shown in the JDK source:

1 public final void setPriority(int newPriority) { 2 ThreadGroup g; 3 checkAccess(); 4 if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) { 5 throw new IllegalArgumentException(); 6 } 7 if((g = getThreadGroup()) ! = null) { 8 if (newPriority > g.getMaxPriority()) { 9 newPriority = g.getMaxPriority(); 10 } 11 setPriority0(priority = newPriority); 13 12}}Copy the code

In Java, threads have priorities ranging from 1 to 10. If the priority is less than 1 or greater than 10, the JDK throws throw new IllegalArgumentException(). Three constants are used in the JDK to preset values that define priorities as follows:

1 public final static int MIN_PRIORITY = 1;
2 public final static int NORM_PRIORITY = 5;
3 public final static int MAX_PRIORITY = 10;
Copy the code

Thread priority features:

  • Inheritance For example, if thread A starts thread B, thread B has the same priority as thread A.
  • Regular high-priority threads are always mostly finished first, but it does not mean that high-priority threads are all finished first.
  • Threads with higher priority randomness don’t always finish first.

Daemon thread

There are two types of Java threads: User threads and Daemon threads. Daemons provide services for other threads to run, such as GC threads. There is essentially no difference between User threads and daemons. The only difference is when the virtual machine leaves: if all User threads leave, there are no more threads to serve and the virtual machine exits.

Public final void setDaemon(Boolean on) public final void setDaemon(Boolean on); But there are a few caveats:

  • Thread. SetDaemon (true) must be in the thread. The start () set before, otherwise you will run out of a IllegalThreadStateException anomalies. You cannot set a running regular thread as a daemon thread. (Note: this is obviously different from daemons. Daemons are created to release the process from the control of the original session + to release the process from the control of the original process group + to release the process from the control of the original control terminal. So the virtual machine language is fundamentally different from the system-level language.
  • New threads created in Daemon threads are Daemon threads. (Again, there is an essential difference: The daemon fork() child is no longer a daemon, although it copies information about the parent process. However, the parent process is not init. Current directory to/”)
  • Not all applications can be allocated to Daemon threads for services such as reading and writing operations or computing logic. This is because the virtual machine may exit before the Daemon Thread arrives.

Synchronization and deadlocks

  • A code block is called a synchronized code block by adding the keyword “synchronized” to it
  • Synchronize code block format
Synchronized {block of code to be synchronized; }Copy the code
  • Synchronized methods, in addition to code blocks can be synchronized, methods can also be synchronized
  • Method synchronization format
Synchronized void method name (){}Copy the code

The interview questions

What is the difference between threads and processes? A: A process is a self-contained runtime environment that can be considered a program or an application. A thread is a task that is executed in a process. Threads are a subset of processes, and a process can have many threads, each performing different tasks in parallel. Different processes use different memory space, and all threads share the same memory space. Not to be confused with stack memory, each thread has a separate stack memory to store local data.

How do you implement threads in Java? A: There are two ways to create a Thread: first, inherit the Thread class and extend the Thread. Implement Runnable interface.

To start a thread, call the run() or start() method? A: To start a thread, call the start() method and make the virtual processor it represents runnable. This means that it can be scheduled and executed by the JVM. It does not mean that the thread will run immediately. The run() method is a callback to be performed after a thread is started.

What is the difference between the Thread class’s sleep() method and the object’s wait() method, which suspend execution? A: The sleep() method is a static method of the Thread class. Calling this method causes the current Thread to suspend execution for a specified time, giving up the CPU to another Thread. However, the lock on the object remains, so it is automatically resumed when the sleep time expires (the Thread returns to the ready state, See thread state transition diagram in question 66). Wait () is a method of the Object class. Calling the Object’s wait() method causes the current thread to abandon the lock on the Object and enter the Object’s wait pool. A thread in the wait pool can be awakened to enter the lock pool only when notify() (or notifyAll()) of an object is called. If the thread recaptures the lock of the object, it enters the ready state.

What is the difference between a thread’s sleep() method and its yield() method? The sleep() method gives other threads a chance to run without considering the priority of the thread, so it gives lower priority threads a chance to run. The yield() method only gives threads of the same or higher priority a chance to run; ② The thread is blocked after executing sleep() and ready after executing yield(). ③ The sleep() method declares to throw InterruptedException, while the yield() method does not declare any exceptions. ④ The sleep() method is more portable than the yield() method, which is related to operating system CPU scheduling.

Name methods related to thread synchronization and thread scheduling. A:

  • Wait () : to cause a thread to wait(block) and release the lock on its holding object;
  • Sleep () : puts a running thread to sleep and is a static method that handles InterruptedException;
  • Notify () : Wakes up a thread that is in the wait state. This method is used to notify a thread that is in the wait state.
  • NotityAll () : wakes up all threads in the waiting state. This method does not lock the object to all threads, but makes them compete. Only the thread that acquired the lock can enter the ready state.

conclusion

The above is some of the basic concepts of multithreading, may be summed up not carefully enough, many inclusive. Some important knowledge points will be summarized separately in the follow-up. Learn multithreading is the basis of high salary, small partners come on together!

reference

“Java multithreaded programming core technology” “Java concurrent programming practice” Java concurrent programming: The use of Thread class summary of Java concurrent programming and thinking about Java multithreaded implementation of three ways