Java multi thread essence

Java multi thread essence

Basic concepts: program, process, thread

  • A program is a set of instructions written in a language. Refers to a set of static code, static objects.
  • A process is an execution of a program, or a running program. It is a dynamic process with its own process of emergence, existence and extinction, that is, the life cycle. (the program is static and the process is dynamic)
  • As a unit of resource allocation, the system allocates different memory areas for each process at run time.
  • A process can be subdivided into threads, which is an execution path within a program. If a process executes multiple threads in parallel at the same time, it supports multithreading.
  • As a unit of scheduling and execution, each thread has an independent running station 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 inter thread communication simple and efficient. However, the shared system resources operated by multiple threads may bring security risks.

Single core CPU and multi-core CPU understanding

  • Single core CPU - in fact, it is a fake multithreading, because in a time unit, it can only perform the task of one thread (but the time unit is too short to feel it). For example, ETC has many lanes, but if there is only one toll collector in the toll station, the CPU is the toll collector. If someone doesn't want to pay, the toll collector will "hang up" him until he thinks about it and wants to pay.
  • If it is a multi-core CPU, it can give better play to the efficiency of multithreading. (today's servers are multi-core)
  • Example: a Java program Exe, in fact, there are at least three threads: main() main thread, gc() garbage collection thread and exception handling thread. Of course, if an exception occurs, it will affect the main thread.

Parallelism and concurrency

  • Parallel - multiple CPU s perform multiple tasks at the same time. For example, many people do different things.
  • Concurrency -- a CPU (using time slices) executes multiple tasks at the same time. Such as the second kill system.

Advantages of using multithreading

  • Improve application response. Especially the graphical interface program.
  • Improve the utilization of computer system CPU.
  • Improve the program structure, divide the long and complex process into multiple threads and run independently, which is conducive to understanding and modification.

When do I need multithreading

  • The program needs to perform two or more tasks at the same time;
  • When the program needs to realize some tasks that need to wait, such as user input, file reading and writing operation, network operation, search and so on;
  • When you need some programs running in the background.

Multithreading creation

Method 1: inherit from Thread class

  1. Create a subclass that inherits from the Thread class
  2. Override run() of Thread class -- declare the operation performed by this Thread in run()
  3. An object that creates a subclass of the Thread class
  4. start() is called from this object

example

// 1. Create a subclass inherited from Thread class
class MyThread extends Thread {
    //2. Override run() of Thread class
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                System.out.println(i);
            }
        }
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        //3. Create objects of subclasses of Thread class
        MyThread t1 = new MyThread();
        //4. Call start(): start method through this object. Functions: start the current thread; Call the run() method of the current thread.
        t1.start();
        // New thread
        MyThread t2 = new MyThread();
        t2.start();
        // The following operations are executed in the main thread. It can be found that they are executed alternately with the t1 object.
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                System.out.println(i + "****************main*****************");
            }
        }
    }
}

be careful:

  • To start a thread, we must call start(), not run().
  • If you start another thread, you must recreate an object of the subclass of tree and call the start() method of the object.

Mode 2: implementation of Runnable interface

  1. Create a class that implements the Runnable interface;
  2. The implementation class implements the abstract method in Runnable: run();
  3. Create an object that implements the class;
  4. Pass this object as a parameter to the constructor of Thread class to create the object of Thread class;
  5. start() is called through the object of the Thread class.

example

// 1. Create a class that implements the Runnable interface
class MyThread implements Runnable {
    //2. Implement the run() method of the Runnable interface
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                System.out.println(i);
            }
        }
    }
}
public class ThreadTest1 {
	public static void main(String[] args) {
		//3. Create an object that implements the class
		MyThread myThread = new MyThread();
		//4. Pass this object as a parameter to the constructor of Thread class and create the object of Thread class
		Thread t1 = new Thread(myThread);
        // Call multiple threads
		Thread t2 = new Thread(myThread);
		//5. Call start() through the object of Thread class
        // Function of start method: 1 Start the thread; 2. Calling the run() -- > of the current thread calls the target of Runnable type.
		t1.start();
		t2.start();
	}
}

Comparison of the two methods

During development: priority: the way to implement Runnable interface

Reason: 1 The implementation method has no limitation of single inheritance of class;

​ 2. The implementation method is more suitable to deal with the situation that multiple threads share data.

Contact: public class Thread implements Runnable

Similarities:

  • Both methods need to override run() and declare the logic to be executed by the thread in run().
  • In both ways, to start a Thread, you call start() in the Thread class.

Common thread methods

  1. start(): start the current thread; Call the run() of the current thread
  2. run(): you usually need to override this method in the Thread class and declare the operation to be performed by the created Thread in this method
  3. currentThread(): a static method that returns the thread executing the current code
  4. getName(): get the name of the current thread
  5. setName(): sets the name of the current thread
  6. yield(): release the execution authority of the current cpu
  7. join(): the thread join() is called in thread a. At this point, thread a enters the blocking state until the thread a is fully executed, and the thread a ends the blocking state.
  8. stop(): obsolete. When this method is executed, the current thread is forced to end.
  9. sleep(Long millitime): lets the current thread sleep for the specified millitime milliseconds. The current thread is blocked for the specified millitime milliseconds.
  10. isAlive(): judge whether the current thread is alive.

thread priority

  1. MAX_ PRIORITY: 10
    MIN PRIORITY: 1
    Norm priority: 5 -- > default priority
  2. How to get and set the priority of the current thread:
    getPriority(): get the priority of the thread
    setpriority(int p): sets the priority of the thread
    Note: the high priority thread should seize the execution right of the low priority thread cpu. But only in terms of probability, high priority threads are executed with high probability. This does not mean that only after the high priority thread is executed, the low priority thread will execute.

Thread life cycle

To implement multithreading, you must create a new Thread object in the main Thread. The Java language uses the objects of Thread class and its subclasses to represent threads. In a complete life cycle, it usually experiences the following five states:

  • New: when the object of a Thread class or its subclass is declared and created, the new Thread object is in the new state
  • Ready: after the thread in the new state is started (), it will enter the thread queue and wait for the CPU time slice. At this time, it has met the running conditions, but it has not been allocated CPU resources
  • Run: when the ready thread is scheduled and obtains CPU resources, it enters the running state. The run() method defines the operation and function of the thread
  • Blocking: in a special case, when the input / output operation is suspended or executed artificially, give up the CPU and temporarily suspend its execution to enter the blocking state
  • Death: the thread has completed all its work, or the thread is forcibly terminated in advance or ends with an exception

Thread synchronization

Examples of thread safety issues

Create three windows to sell tickets, with a total of 100 tickets Use the way to implement the Runnable interface

  1. Problem: in the process of selling tickets, there are duplicate tickets and wrong tickets – > there are thread safety problems.
  2. The reason for the problem: when a thread operates the ticket and the operation has not been completed, other threads participate and operate the ticket.
  3. How to solve it: when a thread a is operating a ticket, other threads cannot participate. The thread can not start to operate the ticket until thread a has finished operating the ticket. This situation cannot be changed even if thread a is blocked.
  4. In Java, we solve the thread safety problem through synchronization mechanism.

Solution

Method 1: synchronize code blocks

synchronized(Synchronization monitor){
	//Code to be synchronized
}

explain:

  1. The code that operates shared data is the code that needs to be synchronized
  2. Shared data: variables operated by multiple threads. For example, a ticket is sharing data.
  3. Synchronization monitor, commonly known as lock. The object of any class can act as a lock.
    Requirement: multiple threads must share the same lock.

Add: in the way of implementing Runnable interface to create multithreading, we can consider using this as the synchronization monitor.

Runnable interface

Take ticket selling as an example to solve the multithreading security problem of Runnable interface implementation

class Window implements Runnable {
    // The number of votes was set to 100
    private int ticket = 100;
    // Set up a synchronization monitor (lock), which is common, and only one can ensure security
    Object obj = new Object();
    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
    			if (ticket > 0) {
        			try {
            			Thread.sleep(100);
        			}catch (InterruptedException e) {
            			e.printStackTrace();
        			}
        			System.out.println(Thread.currentThread().getName() + ":Ticket No.:" + ticket);
        			ticket--;
    			}else {
        			break;
    			}
			}
        }
    }
}
public class Tickets {
    public static void main(String[] args) {
        Window window = new Window();
        Thread t1 = new Thread(window);
        Thread t2 = new Thread(window);
        Thread t3 = new Thread(window);
        t1.setName("Window one");
        t2.setName("Window II");
        t3.setName("Window three");
        t1.start();
        t2.start();
        t3.start();
    }
}
Inherit Thread class
class Window1 extends Thread {
    // The number of votes was set to 100
    private int ticket = 100;
    // Set up a synchronization monitor (lock), which is common, and only one can ensure safety
    Object obj = new Object();
    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
    			if (ticket > 0) {
        			try {
            			Thread.sleep(100);
        			}catch (InterruptedException e) {
            			e.printStackTrace();
        			}
        			System.out.println(Thread.currentThread().getName() + ":Ticket No.:" + ticket);
        			ticket--;
    			}else {
        			break;
    			}
			}
        }
    }
}
public class Tickets1 {
    public static void main(String[] args) {
        
        Window1 t1 = new Window1();
        Window1 t2 = new Window1();
        Window1 t3 = new Window1();
        t1.setName("Window one");
        t2.setName("Window II");
        t3.setName("Window three");
        t1.start();
        t2.start();
        t3.start();
    }
}

Mode 2: synchronization method

If the code that operates on shared data is completely declared in a method, we might as well synchronize this method declaration.

Summary of synchronization methods:
1. The synchronization method still involves the synchronization monitor, but we don't need to explicitly declare it.
2. Non static synchronization method. The synchronization monitor is this
Static synchronization method. The synchronization monitor is the current class itself

Runnable interface
class Window implements Runnable {
    // The number of votes was set to 100
    private int ticket = 100;
    @Override
    public void run() {
        while (true) {
            show();
        }
    }
    // Just add synchronized to the encapsulated method
    // The synchronization monitor is this
    private synchronized void show () {
        if (ticket > 0) {
            try {
                Thread.sleep(100);
            }catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":Ticket No.:" + ticket);
            ticket--;
        }
    }
}
public class Tickets {
    public static void main(String[] args) {
        Window window = new Window();
        Thread t1 = new Thread(window);
        Thread t2 = new Thread(window);
        Thread t3 = new Thread(window);
        t1.setName("Window one");
        t2.setName("Window II");
        t3.setName("Window three");
        t1.start();
        t2.start();
        t3.start();
    }
}
Inherit Thread class
class Window1 extends Thread {
    // Set the number of votes to 100 and set it to static at this time
    private static int ticket = 100;
    @Override
    public void run() {
        while (true) {
            show();
        }
    }
    // Similarly, set the method to static. Since the synchronization monitor is this, if static is not added, there will be three this, namely T1, T2 and T3
    private static synchronized void show () {
        if (ticket > 0) {
            try {
                Thread.sleep(100);
            }catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":Ticket number:" + ticket);
            ticket--;
        }
    }
}

Thread safe singleton mode

Lazy application

public class Bank {
    private Bank () {}
    private static Bank instance = null;
    public static Bank getInstance() {
         //Mode 1: low efficiency
        synchronized (Bank.class) {
            if (instance == null) {
                instance = new Bank();
            }
            return instance;
        }
        //Mode 2: higher efficiency   
        if (instance == null) {
            synchronized (Bank.class) {
                if (instance == null) {
                    instance = new Bank();
                }
            }
        }
        return instance;
    }
}

Thread deadlock

Concept of deadlock

Different threads occupy the synchronization resources needed by the other party and do not give up. They are waiting for the other party to give up the synchronization resources they need, forming a thread deadlock.
After a deadlock occurs, there will be no exception or prompt, but all threads are blocked and cannot continue.

Using Lock to solve thread safety problems

  • Starting from JDK 5.0, Java has provided a more powerful thread synchronization mechanism -- one by explicitly defining synchronization Lock objects to achieve synchronization. Synchronous locks use Lock objects as.
  • java.util.concurrent.locks.Lock interface is a tool that controls multiple threads to access shared resources. Locks provide exclusive access to shared resources. Only one thread can lock the lock object at a time. Threads should obtain the lock object before accessing shared resources.
  • ReentrantLock class implements Lock. It has the same concurrency and memory semantics as synchronized. Reentrantl Lock is commonly used in thread safety control, which can explicitly add and release locks.

example

import java.util.concurrent.locks.ReentrantLock;

public class LockTest {
    public static void main(String[] args) {
        Window2 window = new Window2();
        Thread t1 = new Thread(window);
        Thread t2 = new Thread(window);
        Thread t3 = new Thread(window);
        t1.setName("Window 1");
        t2.setName("Window 2");
        t3.setName("Window 3");
        t1.start();
        t2.start();
        t3.start();
    }
}
class Window2 implements Runnable {
    // The number of votes is set to 100
    private int ticket = 100;
    // 1. Instantiate ReentrantLock
    private ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            try {
                // 2. Call locking method: lock()
                lock.lock();
                if (ticket > 0) {

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + ":Ticket number:" + ticket);
                    ticket--;
                }else {
                    break;
                }
            }finally {
                // Call unlock method: unlock()
                lock.unlock();
            }

        }
    }
}

Similarities and differences between synchronized and Lock

  1. Lock is an explicit lock (manually open and close the lock, don't forget to close the lock), and synchronized is an implicit lock, which is automatically released out of the scope
  2. Lock only has code block lock, and synchronized has code block lock and method lock
  3. Using Lock lock, the JVM will spend less time scheduling threads and perform better. And it has better extensibility (providing more subclasses)

Priority: Lock lock > sync code block > sync method

Thread communication

Three methods involved:

  • wait(): once this method is executed, the current thread enters the blocking state and releases the synchronization monitor.

  • notify(): once this method is executed, one thread waiting will be awakened. If multiple threads are waiting, the one with higher priority will be awakened.

  • notifyALl(): once this method is executed, all threads wait ing will be awakened.

Description: (key points)

  1. The three methods wait(), notify(), notifyAll() must be used in the synchronization code block or synchronization method.
  2. The callers of wait(), notify(), notifyAll() methods must be synchronization code blocks or synchronization monitors in synchronization methods. Otherwise, an exception will occur.
  3. The three methods wait(), notify(), notifyAll() are defined in Java Lang. object class.

Similarities and differences between multithreaded sleep() and wait() methods

The same point: once the method is executed, the current thread can enter the blocking state.
difference:

1) The two methods are declared in different positions: sleep() in Thread class and wait() in object class;
2) The requirements for calling are different: sleep() can be called in any required scenario. wait() must be used in the synchronization code block or synchronization method;
3) About whether to release the synchronization monitor: if both methods are used in the synchronization code block or synchronization method, sleep() will not release the lock, but wait() will release it.

Multithreaded communication: producer consumer

background

The producer gives the product to the clerk, while the customer takes the product from the clerk. The clerk can only hold a fixed number of products at a time (e.g. 20). If the producer tries to produce more products, the clerk will ask the producer to stop. If there is an empty space in the store, the clerk will inform the producer to continue production; If there is no product in the store, the clerk will tell the consumer to wait. If there is a product in the store, he will inform the consumer to take the product.

analysis

1. Is it a multithreading problem? Yes, producer thread, consumer thread
2. Is there shared data? Yes, clerk (or product)
3. How to solve the thread safety problem? Synchronization mechanism, there are three methods
4. Is thread communication involved? yes

code implementation

class Clerk {
    private int productCount = 20;
    // yield a product
    public synchronized void produceProduct() {
        if (productCount < 20) {
            productCount++;
            notify();
            System.out.println(Thread.currentThread().getName() + ":Start production" + productCount + "Products");
        }else {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    // Consumer products
    public synchronized void consumeProduct() {
        if (productCount > 0) {
            System.out.println(Thread.currentThread().getName() + ":Start consumption" + productCount + "Products");
            productCount--;
            notify();
        }else {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
// producer
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 {
                sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.produceProduct();
        }
    }
}
// consumer
class Consumer extends Thread{
    private Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }
    @Override
    public void run() {
        System.out.println(getName() + "Start consuming products......");
        while (true) {
            try {
                sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.consumeProduct();
        }
    }
}
public class ProductTest {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();
        Producer p1 = new Producer(clerk);
        Consumer c1 = new Consumer(clerk);
        Consumer c2 = new Consumer(clerk);
        Consumer c3 = new Consumer(clerk);
        p1.setName("Producer 1");
        c1.setName("Consumer 1");
        c2.setName("Consumer 2");
        c3.setName("Consumer 3");
        p1.start();
        c1.start();
        c2.start();
        c3.start();
    }
}

JDK5.0 new thread creation method

New method 1: implement Callable interface

Compared with the Runnable interface, the Callable interface is more powerful.

  • Compared with the run() method, it can have a return value;
  • Method can throw an exception;
  • Support the return value of generics;
  • You need to use the FutureTask class to get the returned results, for example.
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

// 1. Create a class that implements the Callable interface
class NumThread implements Callable {
    // 2. Implement the call() method and declare the operation to be performed by this thread in call()
    @Override
    public Object call() throws Exception {
        int sum = 0;
        for (int i = 0; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }
}
public class CallableTest {
    public static void main(String[] args) {
        // 3. Create an object of the Callable interface implementation class
        NumThread numThread = new NumThread();
        // 4. Pass the object of this Callable interface implementation class to the FutureTask constructor as an object to create the FutureTask object
        FutureTask futureTask = new FutureTask(numThread);
        // 5. Pass the FutureTask object as a parameter to the constructor of the Thread class, create the Thread object, and call start()
        new Thread(futureTask).start();
        try {
            // 6. Get the return value of the calll method in the Callable
            // get() return value
            Object sum = futureTask.get();
            System.out.println("The sum is:" + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

New method 2: use thread pool

Background: resources that are often created and destroyed and used heavily, such as threads in concurrency, have a great impact on performance.
Idea: create many threads in advance, put them into the thread pool, get them directly when using them, and put them back into the pool after use. It can avoid frequent creation, destruction and reuse. Similar to public transport in life.
Benefits:
・ improved response speed (reduced time to create new threads)
Reduce resource consumption (reuse threads in the thread pool and do not need to be created every time)
・ easy thread management

  • corePoolSize: the size of the core pool
  • maximumPoolSize: maximum number of threads
  • keepAliveTime: when the thread has no task, how long will it last at most and then terminate
  • JDK 5.0 provides thread pool related APIs: executorservice and Executors
  • ExecutorService: the real thread pool interface. Common subclass ThreadPoolExecutor
    void execute(Runnable command): executes task 1 command without return value. It is generally used to execute Runnable command
     < T > Future < T > submit (Callable < T > task): execute a task with a return value. It is generally used to execute Callable
    ・ void shutdown(): close the connection pool
  • Executors: tool class and factory class of thread pool, which are used to create and return different types of thread pools
    ➢Executors.newCachedThreadPool(): create a thread pool that can create new threads as needed
    ➢Executors.newFixedThreadPool(n): create a thread pool with a fixed number of reusable threads
    ➢Executors. Newsinglethreadexecution(): create a thread pool with only one thread
    ➢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 NumberThread implements Runnable {

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

    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            if (i % 2 != 0) {
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}
public class ThreadPool {
    public static void main(String[] args) {
        // 1. Provide a thread pool with a specified number of threads
        ExecutorService service = Executors. newFixedThreadPool(10);

        // Set the thread pool property and find the class first
        System.out.println(service.getClass());
        ThreadPoolExecutor service1 = (ThreadPoolExecutor)service;
        service1.setCorePoolSize(3);
        //service1.setKeepAliveTime();
        //service1.setMaximumPoolSize();
        // 2. To execute the operation of the specified thread, you need to provide the object of the Runnable interface or Callable interface implementation class
        service. execute(new NumberThread());//Suitable for Runnable
        service. execute(new NumberThread1());//Suitable for Runnable
        //service. submit();// Suitable for Callable
        // 3. Close the connection pool
        service.shutdown();
    }
}

Keywords: Java Multithreading

Added by Boz on Mon, 24 Jan 2022 06:47:15 +0200