Java multithreading

Posted by geaser_geek on Sun, 26 Dec 2021 10:56:07 +0100

1. Basic concepts

1.1 distinction among programs, processes and threads:

Program

The essence is a piece of static code and static object.

Process

An execution of a program, or a running program, is a dynamic process.

Process is the unit of resource allocation. The system will allocate different memory areas for each at run time.

Thread

Thread is the unit of scheduling and execution. Each thread has an independent running stack and program counter (PC).

Multiple threads in a process share the same memory unit / memory address space.

Threads in a process can access the same variables and objects.

Note: Java application Exe has at least three threads: main main thread, gc garbage collection thread and exception handling thread.

1.2 parallelism and concurrency

Parallel: multiple CPU s execute multiple tasks at the same time

Concurrency: one CPU (split time slice) executes multiple tasks at the same time

2. Thread creation and startup

2.1Thread class (java.lang.Thread)

Constructor:

Thread(): creates a thread object

Thread(String threadname): create a process and specify the process name

Thread (Runnable target): the process that creates the specified target object, which implements the run method of the Runnable interface

Thrad(Runnable target,String name): create the process of the specified object and specify the name

attribute

static int MAX _PRIORITY ¢ attribute value: 10 ¢ maximum priority

static int MIN _PRIORITY ¢ attribute value: 1 ¢ minimum priority

static int NORM _PRIORITY ¢ attribute value: 5 ¢ normal priority

Method name

static Thread currentThread(): returns the current thread

static void yield(): thread yielding to a higher priority thread

static void sleep(long mills): the thread waits, but an InterruptedException exception will be thrown

void stop(): obsolete, forcing the current thread to stop

void run(): the method to be overridden by the subclass

void start(): start and call the run() method

void join(): calling thread B's join() in thread A will block thread A until thread B completes execution.

boolean isAlive(): judge whether the thread is still running

Boolean islntruopted(): judge whether the thread is interrupted

int getPriority(): return thread priority

viod setPriority(int priority): sets thread priority

String getName(): returns the thread name

void setName(String name): set the name

 

2.3 thread classification

Threads in Java are divided into user threads and daemon threads. The main thread is the user thread, and the gc thread is the guard thread,

The daemon thread will depend on the user thread. When the user thread finishes executing, the daemon thread will also stop executing.

User threads can be set as daemon threads through setDaemon(true). When there are daemon threads in the JVM, the current JVM will exit.

2.4 ways to create threads

There are four ways to create threads, and the last two are in jdk5 Add after 0.

 

2.4. 1. Inherit Thread class (java.lang.Thread)

The defined subclass inherits the Thread class

Override the run method of Thread class in subclass

Create a thread object and call the start method of the object to start the thread (execute the run method)

Note:

If you call the run method manually, it is just a normal method, not a multithreaded mode

The actual method is called by the JVM

A thread object can only call the start method once. If it is called repeatedly, an exception "Iegal Thread State Exception" will be thrown

 

2.4. 2. Implement the Runnable interface (java.lang.runnable)

Q: why implement the Runnable interface?

-Java does not support multi inheritance- It is not intended to override other methods of the Thread class.

Define a class to implement the Runnable interface

 1 package com.imooc.runnable;
 2 class PrintRunnable implements Runnable{
 3 /*
 4  * (non-Javadoc)
 5  * @see java.lang.Runnable#run()
 6  * Core operation
 7  * 1.Define a Class A to implement the Runnable interface
 8  * 2.Implement the run method in Runnable
 9  * 3.Runnable Cannot use getName() method directly
10  * A static method that needs to call Thread is Thread currentThread(), which represents the current Thread, and then calls the gatName() method
11  * Thread.currentThread().getName();
12  * 4.In the main method, first define the object B of class A. class a cannot directly call the start() method. The starting Thread can only be Thread and its subclasses
13  *Therefore, first create the Thread object C and use B as the parameter Thread t1 = new Thread(pr);
14  */
15     @Override
16     public void run() {
17         int i=1;
18         while(i <= 10)
19         System.out.println(Thread.currentThread().getName()+"Running!"+(i++));
20         
21     }
22     
23 }
24 public class Test {
25 
26     public static void main(String[] args) {
27          PrintRunnable pr = new PrintRunnable();
28          Thread t1 = new Thread(pr);
29          t1.start();
30          PrintRunnable pr1 = new PrintRunnable();
31          Thread t2 = new Thread(pr1);
32          t2.start();
33     }
34 
35 }

 

The implementation class needs to override the run method of the Runnable interface

Pass the object of the implementation class as a parameter to the constructor of the Thread class to create a Thread object.

Call the start method of the thread object (start the thread and call the run method of the current thread)

2.4. 3. Implement the callable interface (java.util.concurrent.Callable)

Callable interface

The Callable interface is more powerful than the Runnable interface. Classes that implement the Callable interface need to override the call() method.

The call() method supports generic return values, can throw exceptions, and can obtain the return results with the help of Future Task.

Future interface (java.util.concurrent.Future)

You can cancel the execution results of specific runnable and callable tasks, query whether they are completed, and obtain results

The FutureTask class is the only implementation class of the Future interface.

The FutureTask class is the only implementation class of the Future interface.

FutureTask class implements both Runnable interface and Future interface. It can be executed by the thread as Runnable or callable as Future The return value of call().

2.4. 4. Using thread pool

Background

Resources that are often created and destroyed and used heavily, such as threads in concurrency, have a great impact on performance.

Train of thought

Create multiple threads in advance, put them into the thread pool, obtain them directly when using them, and put them back into the pool after use. Frequent creation can be avoided

Destroy and realize reuse. Similar to public transport in life.

Benefits

Improve reuse. Similar to public transport in life.

Reduce resource consumption (reuse threads in the thread pool and do not need to be created every time)

Easy thread management

 

ExecutorService: thread pool interface. The common implementation class ThreadPoolExecutor can be used to set thread pool properties

method

Void execute (Runnable command): execute a task or command, which is generally used to execute Runnable

Future submit(callable task): execute a task. It is generally used to execute Callable tasks

void shutdown(): closes the thread pool

 

Executors: tool class and factory class of thread pool, which are used to create and return different types of threads

method

static ExecutorService newCachedThreadPool(): create a thread pool that can create new threads as needed

static ExecutorService newFixedThreadPool(n): create a thread pool with a fixed number of reusable threads

static ExecutorService newSingleThreadExecutor(): create a thread pool with only one thread

static ScheduledExecutorService newScheduledThreadPool(n): creates a thread pool that can be scheduled to run or execute periodically after a given delay

2.4. 5 Comparison of thread creation methods

Inheriting Thread class VS implementing Runnadle interface

It is more recommended to implement the Runnable interface in development. This can avoid the limitation of single class inheritance and is more suitable for data sharing among multiple threads

The Thread class actually implements the Runnable interface

 

 

3. Thread life cycle

Thread. Is used in JDK The state class defines several states of a thread

newly build

be ready

function

block

death

4. Thread synchronization

4.1 causes

When multithreading is used to share data, it will cause incomplete operation and destroy data. For an example, see testthreadbug java.

 1 class TestThreaBug {
 2     public static void main(String [] args) {
 3           Ticket ticket = new Ticket();
 4 
 5         //3 Multiple threads sell tickets at the same time
 6         Thread t1 = new Thead(ticket,"t1 Window "");
 7         Thread t2 = new Thead(ticket,"t2 Window ");
 8         Thread t3 = new Thead(ticket,"t3 window");
 9   
10         t1.start();
11         t2.start();
12         t3.start();
13     }
14 }
15 
16 class Ticket implements Ruunable {
17     private int tick = 20;
18 
19      @Override
20     public void run() {
21         while(true) {
22             if(true > 0) {
23                 System.out.println(Thred.currentThread().getName() + "Tickets sold, remaining tickets "
24     }else
25             break;
26         }
27       }
28 }

4.2 solution: synchronized

4.2. 1 synchronization code block

Limitations: when operating synchronous code, only one thread can participate and other threads wait, which is equivalent to a single threaded process with low efficiency.

synchronized(Synchronization monitor){
    //The code that needs to be synchronized, that is, the code that operates the shared data
}

Synchronization monitor, also known as lock. It can be an object of any class, but multiple threads must use the same lock.

 

Problems caused by inter thread communication

wait() method: interrupt the execution of the method and make the thread wait
 notify() method: wake up a thread waiting to end the wait
 notifyAll() method: wakes up all waiting threads to end the wait

 1 package com.imooc.queue;
 2 
 3 public class Queue {
 4     private int n;
 5     boolean flag = false;
 6     public synchronized int get() {
 7         if(!flag) {
 8             try {
 9                 wait();
10             } catch (InterruptedException e) {
11                 // TODO Auto-generated catch block
12                 e.printStackTrace();
13             }
14         }
15         System.out.println("Consumption:"+n);
16         flag = false;//After consumption, there is no data in the container.
17         notifyAll();
18         return n;
19     }
20 
21     public synchronized void set(int n) {
22         if(flag) {
23             try {
24                 wait();
25             } catch (InterruptedException e) {
26                 // TODO Auto-generated catch block
27                 e.printStackTrace();
28             }
29         }
30         System.out.println("Production:"+n);
31         this.n = n;
32         flag = true;//After production, there is no data in the container.
33         notifyAll();
34     }
35     
36     
37 }
 1 package com.imooc.queue;
 2 
 3 public class Producer implements Runnable {
 4     Queue queue;
 5     Producer(Queue queue){
 6         this.queue = queue;
 7     }
 8     @Override
 9     public void run() {
10         int i = 0;
11         while(true) {
12             queue.set(i++);
13             try {
14                 Thread.sleep(1000);
15             } catch (InterruptedException e) {
16                  e.printStackTrace();
17             }
18         }
19     }
20 
21 }
 1 package com.imooc.queue;
 2 
 3 public class Consumer implements Runnable {
 4     Queue queue;
 5     Consumer(Queue queue){
 6         this.queue = queue;
 7     } 
 8     
 9     @Override
10     public void run() {
11         while (true) {
12             queue.get();
13             try {
14                 Thread.sleep(1000);
15             } catch (InterruptedException e) {
16                 // TODO Auto-generated catch block
17                 e.printStackTrace();
18             }
19         }
20     }
21     
22 
23 }
 1 package com.imooc.queue;
 2 
 3 public class Test {
 4 
 5     public static void main(String[] args) {
 6         Queue queue = new Queue();
 7         new Thread(new Producer(queue)).start();
 8         new Thread(new Consumer(queue)).start();
 9 
10     }
11 
12 }

 

1. notifyAll method is to wake up all threads. This thread cannot wake itself up. If it can wake up, it will prove that it is actually running.
2. The notify() method wakes up one thread randomly, and the notifyAll () method wakes up all threads. Because of the randomness of threads, notifyAll() is generally used
 3. This case can be understood as that the producer must produce before the consumer can consume
 It can be controlled by an intermediate boolean variable,
When flag=true, it indicates that the producer has finished production, and there is no need to carry out producer method. Execute wait() method to make the thread wait,
When flag=false, it indicates that the consumer has consumed, and there is no need to perform the consumer method. Execute the wait() method to make the thread wait,
Note: when the consumer method is executed, the consumer consumption is completed. At this time, the consumer thread is running and the producer thread is waiting for blocking. It is necessary to execute notifyAll method to wake up the producer thread, and vice versa.