Java_ Advanced_ Multithreading

Learn from Silicon Valley

1, Multithreading

1. Basic concepts

Program, process, thread

Program: a set of instructions written in a certain language to complete a specific task. It refers to a piece of static code, static object.

Process: an execution process of a program or a running program. It is a dynamic process: it has its own process of emergence, survival and extinction (life cycle).

Thread: a process can be further refined into a thread, which is a series of execution paths within a program.

  • If a process executes multiple threads at the same time. It supports multithreading.
  • As a 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 inter thread communication more simple and efficient. However, the shared system resources operated by multiple threads may bring security risks.

Understanding of single core CPU and multi-core CPU

  • Single core CPU is actually a fake multithreading, because it can only execute the tasks of one process in a time unit. But because the CPU time unit is very short, I can't feel it.
  • If it is multi-core, it can give better play to the efficiency of multithreading.
  • A Java application, java.exe, actually has at least three processes: main() main thread and gc() garbage collection thread. Process threads at once. Of course, if an exception occurs. Will affect the main thread.
  • Concurrency and parallelism
    • Parallel: multiple CPU s execute multiple tasks at the same time.
    • Concurrency: one CPU (using time slice) executes multiple tasks at the same time.

Advantages of using multithreading

  • Improve application performance accordingly. It makes more sense for the graphical interface and enhances the user experience.
  • 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 improvement.

When do I need multithreading

  • The program needs to perform two or more tasks at the same time.
  • When the program needs to implement some tasks that need to wait, such as user input, file read-write operation, network operation, search, etc.
  • When you need some programs running in the background.

2. Creation and use of threads

The jvm running program of Java language runs multiple threads, which is embodied by the java.lang.Thread class.

Characteristics of Thread class:

  • Each Thread completes its operation through the run() method of a specific Thread object. The main body of the run() method is often called the Thread body;
  • Start the Thread through the star() method of the Thread object instead of calling run();

Method 1: inherit from Thread class

Method steps:

1. Create a subclass inherited from Thread class;

2. Override run() -- "of Thread class to declare the operation executed by this Thread in run()

3. Create objects of subclasses of Thread class;

4. Call start() through this object

//1. Create a subclass inherited from thread class
class MyThread extends Thread{
    //2. Override run() of thread class
    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 star() through this object: start the current thread first, and then call run() of the current thread
        t1.start();
        //Problem 1: we can't start thread t1.run() by calling run() directly

        //Problem 2: start another thread. You can't let the thread of star() execute. An error will be reported. You can recreate the object of a thread
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0){
                System.out.println(i + "***********");
            }
        }
    }
}

Method to create anonymous subclass of Thread class:

package threadtest;
//Create two sub threads. One thread traverses an odd number within 100 and the other traverses an even number
public class ThreadTest {
    public static void main(String[] args) {
//        //1. General writing
//        MyThread1 m1 = new MyThread1();
//        MyThread2 m2 = new MyThread2();
//        m1.start();
//        m2.start();
        //2. Method of creating anonymous subclass of Thread class
        new Thread(){
            public void run(){
                for (int i = 0; i < 100; i++) {
                    if(i % 2 == 0){
                        System.out.println(Thread.currentThread().getName() + ":" + i);
                    }
                }
            }
        }.start();
        new Thread(){
            public void run(){
                for (int i = 0; i < 100; i++) {
                    if(i % 2 != 0){
                        System.out.println(Thread.currentThread().getName() + ":" + i);
                    }
                }
            }
        }.start();
    }
}
class MyThread1 extends Thread{
    public void run(){
        for (int i = 0; i < 100; i++) {
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}
class MyThread2 extends Thread{
    public void run(){
        for (int i = 0; i < 100; i++) {
            if(i % 2 != 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

Mode 2:

Method steps:

1. Create a class that implements the Runnable interface;

2. Implement the class to implement 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 and create the object of Thread class

5. Call start() through the object of Thread class

package threadtest;

//The second way to create multithreading is to implement the runnable interface
//1. Create a class that implements the Runnable interface
class MTHread implements Runnable{
    //2. Implement classes to implement abstract methods in interfaces: run()
    public void run(){
        for (int i = 0; i < 100; i++) {
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}
public class ThreadTest3 {
    public static void main(String[] args) {
        //3. Create an object that implements the class
        MTHread mtHread = new MTHread();
        //4. Pass this object as a parameter to the constructor of Thread class and create the object of Thread class
        Thread t1 = new Thread(mtHread);
        t1.setName("Thread 1");
        //5. Call start() a: start Thread b: call run() of current Thread through the object of Thread class -- "call run() method of target of Runnable type
        t1.start();
        //Start another thread traversal
        Thread t2 = new Thread(mtHread);
        t2.setName("Thread 2");
        t2.start();
    }
}

Compare the two ways to create threads

During development: priority: the way to implement Runnable interface

reason:

  • The implementation method has no limitation of single inheritance of classes;
  • The implementation method is more suitable to deal with the situation that multiple threads share data;

Contact: public class Thread implements Runnable

The same point: both methods need to rewrite run(), and describe the logic to be executed by the thread in run().

practice:

package threadtest;

//Example: the ship has three windows to buy tickets, and the total number of tickets is 100
//There is a thread safety problem, which will be solved later
class Window extends Thread{
    private static int ticket = 100;
    public void run(){
        while (true){
            if(ticket > 0){
                System.out.println(getName() + ":Ticket No.:" + ticket);
                ticket--;
            }else {
                break;
            }
        }
    }
}
public class WindowTest {
    public static void main(String[] args) {
        Window t1 = new Window();
        Window t2 = new Window();
        Window t3 = new Window();

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

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

package threadtest;

//Example: the ship has three windows to buy tickets, and the total number of tickets is 100
//There is a thread safety problem, which will be solved later
class Window extends Thread{
    private static int ticket = 100;
    public void run(){
        while (true){
            if(ticket > 0){
                System.out.println(getName() + ":Ticket No.:" + ticket);
                ticket--;
            }else {
                break;
            }
        }
    }
}
public class WindowTest {
    public static void main(String[] args) {
        Window t1 = new Window();
        Window t2 = new Window();
        Window t3 = new Window();

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

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

thread priority

Java scheduling method:

  • Threads with the same priority form a first in and first out queue (first in, first out service). Use time slice strategy
  • For high priority, the preemptive strategy of priority scheduling is used (high priority threads preempt cpu)

Priority level of thread:

  • MAX_PRIORITY: 10
  • MIN_PRIORITY: 1
  • NORM_PRIORITY: 5

Methods involved:

  • getPriority(): return thread priority
  • setPriority(int newPriority): change the priority of the thread

explain:

  • When a thread is created, it inherits the priority of the parent thread
  • Low priority only has a low probability of obtaining scheduling, and is not necessarily called after high priority threads

Related methods of Thread class

  • start(): start the current thread; Call run() of the current thread;
  • 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;
  • currentThread(): a static method that returns the thread executing the current code;
  • getName(): get the name of the current thread;
  • setName(): sets the name of the current thread;
  • yield(): release the execution right of the current cpu;
  • join(): join() in thread a calling thread b. At this point, thread a enters the blocking state, and thread a ends the blocking state until the thread b is fully executed.
  • stop(): obsolete. When this method is executed, the current thread is forced to end.
  • sleep(long millitime): lets the current thread "sleep" for the specified milliseconds. The current thread is blocked for the specified milliseconds.
  • isAlive(): judge whether the current thread is alive.
package threadtest;

class HelloThread extends Thread{
    public void run(){
        for (int i = 0; i < 100; i++) {
            if(i % 2 == 0){
//                try {
//                    sleep(10);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
                System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() + i);
            }
//            if(i % 20 == 0){
//                this.yield();// Release the execution rights of the current cpu
//            }
        }
    }
    //Method for naming threads
    public HelloThread(String name){
        super(name);
    }
}
public class ThreadTest2 {
    public static void main(String[] args) {
        HelloThread h1 = new HelloThread("Thread:1");
//        h1.setName("thread 1");
        //Setting the priority of sub threads does not necessarily have a higher priority than the main thread
        h1.setPriority(Thread.MAX_PRIORITY);
        h1.start();

        //Name the main thread
        Thread.currentThread().setName("Main thread");
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
        for (int i = 0; i < 100; i++) {
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority()+ i);
            }
            if(i == 20){
                try {
                    h1.join();//When thread b is called join() in thread a, thread a is blocked until the thread b is fully executed, and the thread a ends the blocking state.
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println(h1.isAlive());
    }
}

3. Thread life cycle

JDK defines several thread states with Thread.State class;

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 key 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.

4. Thread safety

(1) Introduce

Problem: in the process of selling tickets, there are duplicate tickets and wrong tickets (thread safety problems);

Reason: when a thread operates a ticket, but the operation is not completed, other threads participate and operate the ticket sale;

How to solve this problem: when a thread a is operating a ticket, other threads cannot participate. The thread can not start operating a ticket until thread a has finished operating the ticket. This situation cannot be changed even if thread a is blocked.

In Java, we solve the thread safety problem through synchronization mechanism.

(2) Method 1: synchronize code blocks

Format:

Synchronized (synchronized monitor){

/ / code to be synchronized

}

explain:

  • The code that operates the shared data is the code that needs to be synchronized;
  • Shared data: variables operated by multiple threads. For example, ticket is shared data.
  • Synchronization monitor, commonly known as lock. Any class object can act as a lock. Requirement: multiple threads must share the same lock.
  • In the way of implementing the Runnable interface to create multithreading, we can consider using this as the synchronization monitor.
package threadtest;


//Use the way to implement the Runnable interface
class Window2 implements Runnable{
    private int ticket = 1000;
    Object obj = new Object();//Share the same lock
    public void run(){
        //Object obj = new Object(); / / the same lock is not shared, and the thread is unsafe
        while (true){
            synchronized (this){//this is the object W / / synchronized (obj){
                if (ticket > 0) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":Ticket No.:" + ticket);
                    ticket--;
                } else {
                    break;
                }
            }
        }
    }
}

public class WindowTest3 {
    public static void main(String[] args) {
        Window2 w = new Window2();

        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

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

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


package threadtest;

//Example: the ship has three windows to buy tickets, and the total number of tickets is 100
//Use synchronous code blocks to solve Thread safety problems in the way of inheriting Thread class
//In the way of inheriting Thread class to create multithreads, be careful to use this as the synchronization monitor. You can use class class as the synchronization monitor
class Window3 extends Thread{
    private static int ticket = 100;
    private static Object a = new Object();//Static, common object
    public void run(){
        while (true){
            //synchronized (a) {/ / this can not be used here
            synchronized (Window3.class){//It can also be written like this. Classes are also objects
                if (ticket > 0) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName() + ":Ticket No.:" + ticket);
                    ticket--;
                } else {
                    break;
                }
            }
        }
    }
}

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

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

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


(3) Method 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.

package threadtest;

//Using synchronization method to solve the thread safety problem of implementing Runnable interface

class Window4 implements Runnable{
    private int ticket = 1000;
    public void run(){
        while (true){
                show();
        }
    }
    private synchronized void show(){//Synchronization method (synchronization monitor: this)
        if (ticket > 0) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":Ticket No.:" + ticket);
            ticket--;
        }
    }
}

public class WindowTest5 {
    public static void main(String[] args) {
        Window4 w = new Window4();

        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

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

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



package threadtest;

//Using synchronization method to solve thread safety problems
class Window5 extends Thread{
    private static int ticket = 100;
    public void run(){
        while (true){
            show();
        }
    }
    private static synchronized void show(){//Synchronization monitor is the current class. Windou5.class
//    private synchronized void show() {/ / thread safety problems still exist in synchronization monitors T1, T2 and T3. This solution is wrong
        if(ticket > 0){
            System.out.println(Thread.currentThread().getName() + ":Ticket No.:" + ticket);
            ticket--;
        }
    }
}

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

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

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


Summary of synchronization methods:

  • The synchronization method still involves the synchronization monitor, but we don't need to display the declaration.
  • Non static synchronization method, synchronization monitor: this
  • Static synchronization method, when synchronizing the monitor: the current class itself

(4) Mode 3: lock

  • Starting from JDK5.0, Java has provided a more powerful thread synchronization mechanism -- synchronization is achieved by displaying and defining synchronization Lock objects. Synchronization locks use Lock objects as.
  • The java.util.concurrent.lock.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. ReentratLock is commonly used in thread safety control, which can display locking and releasing locks.
package lock;

//Three ways to solve thread safety problems: Lock lock JDK5.0 NEW

import java.util.concurrent.locks.ReentrantLock;

class Window implements Runnable{
    private int ticket = 100;
    //1. Instantiation
    private ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while(true){
            try{
                //2. Call the locking method lock()
                lock.lock();

                if(ticket > 0){

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

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

    }
}
public class LockTest {
    public static void main(String[] args) {
        Window w = new Window();

        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

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

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

Comparison between synchronized and Lock:

  • Lock is a display 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;
  • Lock only has code block lock, synchronized has code block lock and method lock;
  • Using Lock lock, the JVM will spend less time scheduling threads, have better performance, and have better scalability (provide more subclasses)
  • Priority: Lock -- synchronization code block -- synchronization method

Interview question: what are the similarities and differences between synchronized and lock?

  • Both can solve the thread safety problem;
  • The synchronized mechanism automatically releases the synchronization monitor after executing the corresponding synchronization code
  • Lock needs to manually start synchronization (lock()) and end synchronization (unlock())

5. Thread 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.

resolvent:

  • Special algorithms and principles
  • Minimize the definition of synchronization resources
  • Try to avoid nested synchronization
package sisuo;

//Demonstrate thread deadlock
public class ThreadTest {
    public static void main(String[] args) {
        StringBuffer s1 = new StringBuffer();
        StringBuffer s2 = new StringBuffer();

        new Thread(){
            public void run(){
                synchronized (s1){

                    s1.append("a");
                    s2.append("1");

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

                    synchronized (s2){
                        s1.append("b");
                        s2.append("2");


                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }.start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (s2){
                    s1.append("c");
                    s2.append("3");

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

                    synchronized (s1) {
                        s1.append("d");
                        s2.append("4");

                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }).start();
    }
}

6. Thread communication

Thread communication example: use thread to print 1-100. Thread 1 and thread 2 alternately answer and print.

package threadcommunication;

//Thread communication example: use thread to print 1-100. Thread 1 and thread 2 alternately answer and print.


class Number implements Runnable{
    private int number = 1;

    @Override
    public void run() {
        while (true){

            synchronized (this){//this here is consistent with the objects of notify and wait

                this.notify();//awaken

                if (number <= 100){

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

                    System.out.println(Thread.currentThread().getName() + ":" + number);
                    number++;

                    try {
                        //Make the thread calling the wait method enter the blocking state (Note: executing wait will release the lock)
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }else {
                    break;
                }
            }
        }
    }
}
public class Communicate {
    public static void main(String[] args) {
        Number number = new Number();
        Thread t1 = new Thread(number);
        Thread t2 = new Thread(number);

        t1.setName("Thread 1");
        t2.setName("Thread 2");

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

Three methods involved

wait(): once this method is executed, the current thread will enter the blocking state and release the synchronization monitor.

notify(): once this method is executed, a thread waiting will be awakened. If more than one thread is waiting, it wakes up the one with higher priority.

notifyAll(): once the method is executed once, it will wake up all threads that do not wait.

explain

  • The three methods wait(), notify(), notiyAll() must be used in the synchronization code block or synchronization method.
  • The callers of the three methods wait(), notify(), notiyAll() must be synchronization code blocks or synchronization monitors in synchronization methods. Otherwise, an IllegalMonitorStateException will appear;
  • The three methods wait(), notify(), notiyAll() are defined in the java.lang.Object class.

Similarities and differences between sleep() and wait()

  • Same: once the method is executed, the current thread can enter the blocking state.
  • Different:
    • The positions of the two method declarations are different: sleep() is declared in the Thread class, and wait() is declared in the Obje class
    • 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;
    • About whether to release the synchronization monitor: if both methods are used in the synchronization code block or synchronization method, sleep() will not be released and wait() will be released;

7.JDK5.0 adds two new thread creation methods

Implement Callable interface

Callable is more powerful than Runnable:

  • 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 FutureTask class to obtain the returned results, for example;

Supplement: Future interface

  • You can cancel the execution results of specific Runnable and Callable tasks, query whether they are completed, obtain results, etc;
  • FutureTask is the only implementation class of Future interface;
  • FutureTask also implements Runnable and Future interfaces. It can be executed by the thread as Runnable and get the return value of Callable as Future;
package threadnew;

//The third way to create a thread is to implement the Callable interface JDK5.0


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

//1. Create an implementation class that implements Callable
class NumThread implements Callable{

    //2. Implement the call method and declare the operation to be executed by the secondary thread in call()
    @Override
    public Object call() throws Exception {//Equivalent to run method
        int sum = 0;
        for (int i = 1; i < 100; i++) {
            if (i %2 == 0){
                System.out.println(i);
                sum += i;
            }
        }
        return sum;//Packing
    }
}

public class newThread {
    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 to create the Thread object. And call start()
        new Thread(futureTask).start();

        //(6) . get the return value of call() in Callable
        try {
            //The return value of get() is the return value of call() overridden by the FutureTask constructor parameter Callable implementation class
            Object sum = futureTask.get();
            System.out.println("The sum is:" + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

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.

Benefits:

  • Increase the corresponding speed (reduce the 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 a thread has no task, how long it can be held for at most, and then it will terminate;

Thread pool related API s:

  • JDK5.0 provides thread pool related API s: ExecutorService and Executor;

  • ExecutorService: the real thread pool interface. Common subclass ThreadPoolExecutor

    • void execute(Runnable command): executes a task / command without a return value. It is generally used to execute Runnable;
    • < T > Future < T > submit (Callable < T > task): when executing a task, there is a return value. Generally, Callable is executed again;
    • void shutdown(): close the connection pool;
  • Executors: tool class and thread pool factory class, which are used to create and return different types of threads

    • 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.newSingleThreadExecutor(): create a thread pool with only one thread;
    • Executors.newScheduledThreadPool(n): create a thread pool that can schedule commands to run or execute periodically after a given delay;
package threadnew;

//Four ways to create threads: thread pool
import java.util.concurrent.Executor;
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 properties of thread pool (reflect management)
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        service1.setCorePoolSize(15);
//        service1.setKeepAliveTime();


        //2. To execute the specified thread operation, you need to provide an interface that implements Runnable or an object that implements the class of Callable interface
        service.execute(new NumberThread());//Suitable for Runnable
        service.execute(new NumberThread1());//Suitable for Runnable

//        service.submit(Callable callable);// Suitable for callable

        //3. Close the connection pool
        service.shutdown();
    }
}

6. Cases

package threadexercise;

//Use inheritance
//The bank has an account. Two depositors deposit 3000 yuan into the same account for three times. Print account balance after each deposit
class Account{
    private double balance;
    public Account(double balance){
        this.balance = balance;
    }
    //save money
    public synchronized void deposit(double amt){//this points to account
        if(amt > 0){
            balance +=amt;

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

            System.out.println(Thread.currentThread().getName() + "Successfully saved, the balance is:" + balance);
        }
    }
}
class Customer extends Thread{

    private  Account account;
    public Customer(Account acct){
        this.account = acct;
    }

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            account.deposit(1000);
        }
    }
}
public class AccountTest {
    public static void main(String[] args) {
        Account acct = new Account(0);
        Customer c1 = new Customer(acct);
        Customer c2 = new Customer(acct);

        c1.setName("nail");
        c2.setName("B");

        c1.start();
        c2.start();
    }
}
package threadexercise;

import java.util.concurrent.locks.ReentrantLock;

//How to use lock lock
//The bank has an account. Two depositors deposit 3000 yuan into the same account for three times. Print account balance after each deposit
class Account2{
    public double balance;

    public Account2(double balance){
        this.balance = balance;
    }
    public double getBalance(){
        return this.balance;
    }
}
class Customer2 extends Thread{

    private  Account2 account;
    public Customer2(Account2 acct){
        this.account = acct;
    }
    //1. Instantiation
    private static ReentrantLock lock = new ReentrantLock();//Static is very important here. Without static, the lock is not unique. Every time a Customer is created, there is a lock
    @Override
    public void run() {


        try{
            //2. Call the locking method lock()
            lock.lock();
            for (int i = 0; i < 3; i++) {
                account.balance += 1000;

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

                System.out.println(Thread.currentThread().getName() + "Successfully saved, the balance is:" + account.balance);
            }
        }finally {

            //3. Call unlocking method
            lock.unlock();
        }
    }
}
public class AccountTest2 {
    public static void main(String[] args) {
        Account2 acct = new Account2(0);
        Customer2 c1 = new Customer2(acct);
        Customer2 c2 = new Customer2(acct);

        c1.setName("nail");
        c2.setName("B");

        c1.start();
        c2.start();
    }
}

Posted on Sun, 24 Oct 2021 06:09:18 -0400 by batfastad