Java Basics - multithreading

Thread concept

  1. Concept of program, process and thread
    (1) is a set of instructions written in a certain language to complete a specific task. Refers to one
    Static code, static object.
    (2) it is an execution process of a program or a running program. Is a dynamic
    The process of: it has its own process of emergence, existence and extinction—— life cycle
    a. for example: QQ in operation, MP3 player in operation
    b. the program is static and the process is dynamic
    c. as the unit of resource allocation, the system will allocate different memory areas for each process when running
    (3) the process can be further refined into threads, which is an execution path within a program.
    A. if a process executes multiple threads in parallel at the same time, it supports multithreading
    b. as the unit of scheduling and execution, each thread has an independent running stack and program counter (pc), and the overhead of thread switching is small
    Multiple threads in a process share the same memory unit / memory address space = = > they allocate objects from the same heap and can access the same variables and objects. This makes the communication between threads easier and more efficient. However, the shared system resources operated by multiple threads may bring security risks

2. Each thread has its own stack (Java stack) and program counter, and each process has its own method area and heap (that is, multiple threads of a process will share this method area and heap)

3. A Java application Exe, there are at least three threads: main() main thread, gc() garbage collection thread and garbage disposal thread.

4. Parallelism and Concurrency:
(1) parallel: multiple CPU s execute multiple tasks at the same time
(2) Concurrency: one CPU (using time slice) executes multiple tasks at the same time

Creation and use of threads

1. Multi Thread creation method 1: inherit from Thread class
(1) create a subclass inherited from Thread class
(2) Rewrite run() = = = > of Thread class and declare the operation executed by this Thread in run()
(3) create subclass objects of Thread class
(4) call start() through this object

public class ThreadTest {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.start();
        for(int i=0;i<100;i++){
            System.out.println("hello");
        }

    }
}

class MyThread extends Thread{
    @Override
    public void run() {
        for(int i = 0;i < 100;i++){
            if(i%2==0){
                System.out.println(i);
            }
        }
    }
}
******************************************************************results of enforcement
hello
0
2
hello
hello
hello
. . . . . . 

The above execution results show that in the execution of the main() method, it is not executed sequentially, that is, a new thread is indeed created.

2. Description of start() method:
(1) start the current thread
(2) call the run() of the current thread
(3) you cannot start a thread through the run() method. It must be start()
(4) a thread object can only call the start() method once. To create a new thread, you need to create a new thread object and call the start() method again, otherwise it will report "illegalThreadStateException"

3. Practice

public class ThreadTest2 {
    public static void main(String[] args) {
        //Create two sub threads, one traversing an odd number within 100 and one traversing an even number within 100
        //Use the writing method of anonymous subclass
        new Thread(){
            @Override
            public void run() {
                for(int i=0;i<100;i++){
                    if(i%2==0){
                        System.out.println(Thread.currentThread().getName()+":"+i);
                    }
                }
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                for(int i=0;i<100;i++){
                    if(i%2==1){
                        System.out.println(Thread.currentThread().getName()+":"+i);
                    }
                }
            }
        }.start();

    }
}
**************************************************************************results of enforcement
. . . . . . . 
Thread-0:78
Thread-0:80
Thread-0:82
Thread-1:1
Thread-0:84
Thread-0:86
Thread-0:88
. . . . . . . 

4. Common methods in threads
(1) start(): start the thread and execute the run() method of the thread
(2) run(): the operation performed by a thread when it is scheduled
(3) String getName(): returns the name of the current thread
(4) void setName(String name): sets the name of the current thread
(5) static Thread currentThread(): returns the current Thread. In the Thread subclass, it is equivalent to this, which is usually used for the main Thread and Runnable implementation class.
(6) yield(): release the execution right of the current CPU
(7) join(): the join() method of calling thread b in thread a, when thread a enters the blocking state, and the thread b starts executing, knowing that the thread b completes execution, and the thread a ends the blocking state.
(8) stop(): when this method is executed, the current thread is forcibly terminated. Deprecated
(9) sleep(long millitime): let the current thread enter the "sleep" (blocking) state for millitime milliseconds.
(10) isAlive(): judge whether the current thread is alive

5. Thread scheduling: high priority threads in Java seize the CPU, and the same priority adopts the first come first service strategy.

6. Thread priority in Java:
    (1)MAX_PRIORITY: 10
    (2)MIN _PRIORITY: 1
    (3)NORM_PRIORITY: 5 (default priority)
(4) getPriority(): returns the thread priority value
(5) setPriority(int newPriority): change the priority of the thread
(6) Note: when creating a thread, it inherits the priority of the parent thread. Low priority is only a low probability of obtaining scheduling, and it is not necessarily called after high priority threads

7. Multi thread creation mode 2: implement Runnable interface
(1) create a class that implements the Runnable interface
(2) the implementation class implements the abstract method run() in Runnable
(3) create an object that implements the class
(4) pass this object as a parameter to the constructor of Thread class and create the object of Thread class
(5) call the start() method through the object of Thread class. ① Start the Thread ② call the run() method of the current Thread = = > call the run() method of the target of Runnable type

public class RunnableTest {
	public static void main(String[] args) {
		Mythread2 t1 = new Mythread2();
		
		Thread t2 = new Thread(t1);
		t2.run();
		
	}
}

class Mythread2 implements Runnable{
	@Override
	public void run() {
		for(int i=0; i<100; i++) {
			if(i%2==0) {
				System.out.println(i);
			}
		}
	}
}

Check the source code of Thead class. The run() method is declared as follows

@Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

Find the target, which is a Runnable type data. The constructor can pass in the object of the Runnable implementation class to initialize the target. In this way, when the run() method is called through the Thread class object, the run() method of the incoming Runnable implementation class is called.

/* What will be run. */
private Runnable target;

public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

8. Use two ways to create multithreading to sell 100 tickets in three windows. The following two cases (thread safety problems currently exist)

public class ThreadTest {
    public static void main(String[] args) {
        MyThread1 t1 = new MyThread1();
        MyThread1 t2 = new MyThread1();
        MyThread1 t3 = new MyThread1();

        t1.setName("Window 1");
        t2.setName("Window 2");
        t3.setName("Window 3");
        t3.start();
        t1.start();
        t2.start();


    }
}

class MyThread1 extends Thread{
    private static int ticket = 100;
    @Override
    public void run() {
        while (true){
            if(ticket > 0){
//                try {
//                    Thread.sleep(50);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
                System.out.println(getName()+":"+ticket);
                ticket--;
            }else{
                break;
            }
        }
    }
}
public class RunnableTest {
    public static void main(String[] args) {
        MyThread2 m1 = new MyThread2();
        //Only one object is initialized, so there is no need to add the static keyword to the ticket

        Thread t1 = new Thread(m1,"Window 1");
        Thread t2 = new Thread(m1,"Window 2");
        Thread t3 = new Thread(m1,"Window 3");

        t1.start();
        t2.start();
        t3.start();
    }
}

class MyThread2 implements Runnable{
    private int ticket = 100;

    @Override
    public void run() {
        while (true){
            if(ticket > 0){
//                try {
//                    Thread.sleep(50);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }

                System.out.println(Thread.currentThread().getName()+":"+ticket);
                //Because it is not a subclass of Thread, the previous part should be written when getName
                ticket--;
            }else{
                break;
            }
        }
    }
}

9. Compare the two creation methods:
(1) the method of realizing Runnable interface is preferred in development.
(2) reasons:
① this method has no limitation of single inheritance
② this method is more suitable for dealing with the situation that multiple threads enjoy shared data.
(3) the Thread class also implements the Runnable interface

10. Thread life cycle
In the Thread class, there is an internal class State, which defines several states of the Thread. In the life cycle of a Thread, there are the following states.
(1) create
(2) ready
(3) operation
(4) blocking
(5) death

Thread safety issues

1. Thread safety problem: when multiple threads operate to share data, data conflict may occur, resulting in thread safety problem. For example, in the previous example of selling tickets, there will be duplicate tickets and wrong tickets (the effect of sleep() is better and obvious).
(1) causes of thread safety problems: when a thread is operating shared data, other threads also participate.
(2) how to solve it: when a thread a is operating a shared data, other threads are not allowed to participate in it. Other threads cannot start to operate the shared data until the operation of thread a is completed. Even if thread a enters the blocking state, other threads have to wait.
(3) only write operation will have thread safety problems, and read operation will not lead to thread safety problems.

2.Java solves thread safety problems through synchronization mechanism:
(1) mode 1: synchronous code block

synchronized(Synchronization monitor){
	//Code blocks that need to be synchronized
}

a. the code that operates the shared data is the code block that needs to be synchronized. Synchronized contains too many or too few synchronized code blocks.
b. shared data: data operated by multiple threads. Such as ticket
c. synchronous monitor: commonly known as lock. The object of any class can act as a lock.
d. requirements: multiple threads using the same resource should share the same lock
(2) mode 2: synchronization method
A. if the code that operates on shared data happens to be declared in a method, you might as well declare the method synchronous
b. the synchronization method also has a lock. The lock of a non static method is this, and the lock of a static method is the class itself.
(3) Note: in the way of inheriting Thread class, the class itself can generally be used as a synchronization monitor. Runnable is implemented by using this as a synchronization monitor.

3. Use synchronization code block and synchronization method to solve the thread safety problem of selling tickets at the same time in three windows:
(1) how to synchronize code blocks

class MyThread2 implements Runnable{
    private int ticket = 100;

    private Object obj = new Object();
    @Override
    public void run() {
        while (true){
            synchronized (this){ //Take the object of MyThread2 class as the synchronization monitor
//            synchronized (obj){
                if(ticket > 0){
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":"+ticket);
                    ticket--;
                }else{
                    break;
                }
            }
        }
    }
}
*********************************************************************************
class MyThread1 extends Thread{
    private static int ticket = 100;

    private static Object obj = new Object();//The synchronization monitor for multiple threads must be the same object
    @Override
    public void run() {
        while (true){
            synchronized (MyThread1.class){
                /*You can use the MyThread1 class itself as a synchronization monitor,
                Because the method of inheriting Thread class will create multiple inherited class objects,
                Because the Class itself is actually an object. Is an object of Class.
                 */
//            synchronized (obj){
                if(ticket > 0){
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName()+":"+ticket);
                    ticket--;
                }else{
                    break;
                }
            }
        }
    }
}

(2) synchronization method

class MyThread2 implements Runnable{
    private int ticket = 100;

    private Object obj = new Object();
    @Override
    public void run() {
        while (true){
            func();
        }
    }

    public synchronized void func(){  //The synchronization monitor for non static methods is this
        if(ticket > 0){
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":"+ticket);
            ticket--;
        }
    }

}
**********************************************************************************
class MyThread1 extends Thread{
    private static int ticket = 100;

    private static Object obj = new Object();
    @Override
    public void run() {
        while (true){
            func();
        }
    }

    public static synchronized  void func(){  //The synchronization monitor for static methods is the class itself
        if(ticket > 0){
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":"+ticket);
            ticket--;
        }
    }

}

4. Advantages and disadvantages of thread synchronization:
(1) advantages: it solves the problem of thread safety
(2) disadvantages: in the synchronized code, only one thread can execute at the same time, which is equivalent to a single thread, reducing the efficiency

5. Solve the thread safety problem of lazy singleton mode
(1) problem description: lazy singleton, originally written, may cause multiple objects to be created inside the getInstance method if the getInstance method is called inside the multi-threaded run() method.

public static Order getInstance() {
	if(instance != null) {
	//If thread a has entered the if statement but has not yet created an object,
	//If the b thread also makes a judgment, it will also enter the if statement, resulting in thread safety problems
		instance = new Order();
	}
	return instance;
}

(2) solution:
Directly declare the getInstance method as synchronized

//Directly declare the getInstance method as synchronized
public static synchronized Order getInstance(){
        if(instance == null){
            instance = new Order();
        }
        return instance;
    }

How to synchronize code blocks

//Method 1: when calling the getInstance method, multiple threads will wait to obtain the lock and return to the instance after executing the synchronization code block. The efficiency is too low
public static Order getInstance(){
        synchronized(Order.class){
            if(instance == null){
                instance = new Order();
            }
            return instance;
        }
    }

//Method 2: first make a judgment. If it passes, then obtain the lock. Otherwise, it will directly return to the instance.
//Because judgment will be made inside the lock, there will be no thread safety problem. Slightly more efficient
public static Order getInstance(){
    if(instance == null){
      synchronized(Order.class){
           if(instance == null){
               instance = new Order();
           }
       }
    }
    return instance;
}

6. Thread deadlock:
(1) different threads occupy the synchronization resources required by the other party and do not give up. They are waiting for the other party to give up
The synchronization resources you need form a thread deadlock
(2) after deadlock occurs, there will be no exception or prompt, but all threads are in
Blocking status, unable to continue
(3) solutions: special algorithms and principles; Minimize the definition of synchronization resources; Try to avoid nested synchronization

7. Three ways to solve thread safety problems: Lock = = > jdk5 0 NEW

class Mythread implements Runnable{
    private int ticket = 100;
    private ReentrantLock lock = new ReentrantLock();
    /*If you create multiple threads by inheriting the Thread class, you should also follow the principle of using the same lock,
    You need to declare lock as static
    */
    @Override
    public void run() {
        while (true){
            try {
                lock.lock();
                if(ticket>0){
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":"+ticket);
                    ticket--;
                }else{
                    break;
                }
            } finally {
                lock.unlock();
            }
        }
    }
}

8. The difference between synchronized and lock() methods:
synchronized automatically releases the lock after executing the corresponding synchronization code; lock() needs to start synchronization manually, and it needs to end manually at the end.

9. Recommended sequence: lock() = = > synchronization code block = = = > synchronization method

Thread communication

1. Inter thread communication:
(1) there are three methods involved: wait(), notify(), and notifyall()
(2) wait(): after executing this method, the thread calling the method will enter the blocking state and release the lock.
(3) notify(): after executing this method, a thread that is waiting () will be awakened. If multiple threads are waiting (), the one with the highest priority will be awakened.
(4) notifyall(): after executing this method, all threads that are waiting () will be awakened

public class CommunicationTest {
    public static void main(String[] args) {
        Number number = new Number();

        Thread t1 = new Thread(number,"Thread one");
        Thread t2 = new Thread(number,"Thread two");

        t1.start();
        t2.start();
    }
}

class Number implements Runnable{
    private int num = 1;

    @Override
    public void run() {

        while (true){
            synchronized (this) {
                notify();
                if (num <= 100){
                    System.out.println(Thread.currentThread().getName()+":"+num);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    num++;
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

be careful:
(1) wait(), notify(), and notifyall() can only be used in synchronization code blocks or synchronization methods. (that is, it cannot be used in lock())
(2) the callers of wait(), notify(), and notifyall() methods must be synchronized synchronous monitors, otherwise an IllegalMonitorStatemenException will be reported
(3) wait(), notify(), and notifyall() are implemented in Java Defined under lang.Object class

2. Similarities and differences between sleep() and wait()
(1) the same point: once the method is executed, the current thread will enter the blocking state
(2) differences:
a. different declaration positions: sleep() is declared in Thread class and wait() is declared in Object class
b. different call requirements: sleep() can be called anywhere, while wait() can only be called in synchronous code blocks or synchronous methods
c. about whether to release the synchronization monitor: if both methods are called in the synchronization code block and synchronization method, sleep() will not release the synchronization monitor, while wait() will release it

3. Producer consumer issues

class Clerk{  //As the shared data, the quantity of goods in Clerk is exactly the shared data, so the method of operating num is provided in Clerk
    private int num = 0;
    public synchronized void produceProduct(){
        //The two methods of this synchronization are direct synchronization
        //However, we only created one Clerk object, so the thread is safe
        if(num < 20){
            num++;
            /*
            It is assumed that there are 9 products before production. After one production, it should be displayed that there are currently 10 products,
            However, if there is a blockage here and the consumer buys a product at this time. It will lead to one production on the producer's side,
            The number of products has not changed. After one consumption, the number of products has not changed. Therefore, thread synchronization is required.
             */
            System.out.println("The producer has produced a product and currently has"+num+"Products");
            notify();//Once a product is produced, consumers can be awakened
        }else{
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public synchronized void consumeProduct(){
        if(num > 0){
            num--;
            System.out.println("The consumer has purchased a product and currently has"+num+"Products");
            notify();
        }else{
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

class Producer extends Thread{
    private Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        System.out.println(getName()+"Start production");
        while (true){
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.produceProduct();
        }
    }
}

class Customer extends Thread{
    private Clerk clerk;

    public Customer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        System.out.println(getName()+"Start consumption:");
        while (true){
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.consumeProduct();
        }
    }
}


public class ProductTest {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();

        Producer producer = new Producer(clerk);
        producer.setName("Producer 1");
        Customer customer = new Customer(clerk);
        customer.setName("Consumer 1");

        producer.start();
        customer.start();

    }
}

Two new thread creation methods are added

1.JDK5.0 new creation method
(1) add method 1: implement Callable interface
(2) new method 2: use thread pool

2. How to implement Callable
(1) compared with Runnable, Callable has stronger functions
A. compared with the run() method, it can have a return value
b. the method can throw an exception
c. support the return value of generics
d. you need to use FutureTask class, for example, to get the returned results
(2) Future interface
a. you can cancel the execution results of specific Runnable and Callable tasks, query whether they are completed, obtain results, etc
b. FutrueTask is the only implementation class of Futrue interface
c. FutureTask also implements Runnable and Future interfaces. It can be executed by the thread as Runnable or get the return value of Callable as Future

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

//1. Create the implementation class of Callable interface
class NumberThread implements Callable{
    int sum;
    //2. Rewrite the call method (the call method can throw exceptions and return values)
    @Override
    public Object call() throws Exception {
        for(int i=1; i<100; i++){
            if(i%2==0){
                System.out.println(i);
                sum+=0;
            }
        }
        return sum;
    }
}

public class CallableTest {
    public static void main(String[] args) {
        //3. Create Callable implementation class object
        NumberThread numberThread = new NumberThread();
        //4. Transfer the Callable implementation class object into the FutureTask construction method to create the FutureTask class object
        FutureTask futureTask = new FutureTask(numberThread);
        //5. Pass the FutureTask object as a parameter to the Thread constructor, create the Thread object and call the start() method
        new Thread(futureTask).start();//FutureTask implements the Future interface and Runnable interface, so it can be passed in

        try {
            Object sum = futureTask.get();
            //Get the return value of the call method rewritten in the Callable implementation class of futureTask through the get() method
            System.out.println("The sum is:"+sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}

3. How to use the thread pool: create multiple threads in advance, put them into the thread pool, get them directly when using them, and finish using them
Put it back into the pool. It can avoid frequent creation, destruction and reuse. Advantages of using thread pool:
(1) improve the response speed (reduce the time to create a new thread)
(2) reduce resource consumption (reuse threads in the thread pool and do not need to be created every time)
(3) facilitate thread management
a. corePoolSize: the size of the core pool
b. maximumPoolSize: maximum number of threads
c. keepAliveTime: when the thread has no task, how long will it last at most and then terminate

4. API related to thread pool: JDK 5.0 provides API related to thread pool: ExecutorService and Executors
(1) ExecutorService: the real thread pool interface. Common subclass ThreadPoolExecutor
a. void execute(Runnable command): executes a task / command without a return value. It is generally used to execute Runnable commands
b. Future submit(Callable task): execute a task with a return value. It is generally used to execute Callable tasks
c. void shutdown(): close the connection pool
(2) Executors: tool class and factory class of thread pool, which are used to create and return different types of thread pools
              a. Executors.newCachedThreadPool(): create a thread pool that can create new threads as needed
              b. Executors.newFixedThreadPool(n); Create a reusable thread pool with a fixed number of threads
              c. Executors.newSingleThreadExecutor(): create a thread pool with only one thread
              d. Executors.newScheduledThreadPool(n): creates a thread pool that can schedule commands to run after a given delay or execute periodically.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//import java.util.concurrent.ThreadPoolExecutor;

class NumThread implements Runnable{

    @Override
    public void run() {
        for(int i=0; i<=100; i++){
            if(i%2==0){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}

class NumThread1 implements Runnable{

    @Override
    public void run() {
        for(int i=0; i<=100; i++){
            if(i%2!=0){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}

public class PoolTest {
    public static void main(String[] args) {
        //1. Provide a thread pool with a specified number of threads
        ExecutorService service = Executors.newFixedThreadPool(10);

        //Set thread pool properties as follows
        /*ThreadPoolExecutor service1 = (ThreadPoolExecutor)service;
        service1.setCorePoolSize(5);
        service1.setKeepAliveTime();*/

        //2. Execute the operation of the specified thread. You need to provide an object that implements the Runnable interface or the Callable interface implementation class
        service.execute(new NumThread());//For Runnable
        service.execute(new NumThread1());//For Runnable
//        service.submit();// For Callable
        //3. Close the connection pool
        service.shutdown();
    }
}

Keywords: Java Multithreading

Added by lookee on Fri, 18 Feb 2022 06:37:58 +0200