Java learning route (20) - multithreading

1. Thread

1. Concept: It is an execution path inside a program

2. Classification

  • Single-threaded: There is only one path of execution in the program
  • Multithreading: Multiple paths of execution in a program

Second, the creation of multi-threaded

1. The concept of Thread: Java represents a thread through the java.lang.Thread class. According to the object-oriented principle, the Thread class provides a multi-threaded implementation method.

2. Method 1: Inherit the Thread class

(1) Implementation process

  • Define a thread subclass to inherit the Thread class and override the run() method
  • Create a thread implementation class object
  • Call the thread object start() method to start the thread (the run() method is still executed after startup)

(2) Example of use

public class ThreadDemo {
    
    
    public static void main(String[] args) {
    
    
        MyThread thread = new MyThread("线程1运行中...");
        MyThread thread2 = new MyThread("线程2运行中...");
        thread.start();
        thread2.start();
    }
}

class MyThread extends Thread{
    
    

    private String message;

    public MyThread(){
    
    }

    public MyThread(String message){
    
    
        this.message = message;
    }
    @Override
    public void run() {
    
    
        for (int i = 0; i < 10; i++) {
    
    
            System.out.println(message);
        }
    }
}

/*打印输出*/
线程2运行中...
线程2运行中...
线程2运行中...
线程2运行中...
线程2运行中...
线程1运行中...
线程1运行中...
线程1运行中...
线程1运行中...
线程1运行中...
线程1运行中...
线程1运行中...
线程1运行中...
线程1运行中...
线程1运行中...
线程2运行中...
线程2运行中...
线程2运行中...
线程2运行中...
线程2运行中...

(3) Advantages and disadvantages

  • Pros: Easy to code
  • Disadvantages: The thread class inherits the Thread class and cannot inherit other classes, which is not conducive to expansion

(4) Related issues

Q: Can the main thread task be placed before the child thread?
Answer: If you want to implement multi-threading, then the task of the main thread must be placed after the sub-thread is started, otherwise the CPU will execute the task of the main thread first, and then execute the sub-thread.

Q: Why not call run() directly, but call start()?
Answer: If run() is called, it will be executed as a normal method. In fact, it is still single-threaded. After start() is turned on, it will be considered as multi-threaded execution.

3. Method 2: Implement the Runnable interface

(1) Implementation method

  • Define a thread task class to implement the Runnable interface and override the run() method
  • Create thread task class object
  • Deliver the thread task class object to Thread for processing
  • Call start() to start the thread

(2) Example of use

public class ThreadDemo {
    
    
    public static void main(String[] args) {
    
    
        RunnableThread run = new RunnableThread("线程1运行中...");
        RunnableThread run1 = new RunnableThread("线程2运行中...");
        Thread t = new Thread(run);
        Thread t1 = new Thread(run1);
        t.start();
        t1.start();
    }
}

class RunnableThread implements Runnable{
    
    

    private String message;

    public RunnableThread(){
    
    }

    public RunnableThread(String message){
    
    
        this.message = message;
    }

    @Override
    public void run() {
    
    
        System.out.println(message);
    }
}

/*打印输出*/
线程2运行中...
线程2运行中...
线程2运行中...
线程2运行中...
线程2运行中...
线程1运行中...
线程1运行中...
线程1运行中...
线程1运行中...
线程1运行中...
线程1运行中...
线程1运行中...
线程1运行中...
线程1运行中...
线程1运行中...
线程2运行中...
线程2运行中...
线程2运行中...
线程2运行中...
线程2运行中...

(3) Advantages and disadvantages

  • Advantages: The thread task class only implements the interface, can inherit other classes and implement other interfaces, and has strong scalability
  • Disadvantages: There is an extra layer of object packaging for programming. If the thread has an execution result, it cannot be returned directly.

(4) Anonymous inner class implementation scheme

public class ThreadDemo {
    
    
    public static void main(String[] args) {
    
    
        Thread t1 = new Thread(()->{
    
    
            for (int i = 0; i < 10; i++) {
    
    
                System.out.println("子线程1运行中");
            }
        });

        Thread t2 = new Thread(()->{
    
    
            for (int i = 0; i < 10; i++) {
    
    
                System.out.println("子线程2运行中");
            }
        });

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

4. Method 3: Implement the Callable interface

(1) Usage scenario: When the thread needs to return the result directly.

(2) Implementation method

  • get task object
    • Define the class to implement the Callable interface, rewrite the call method, and encapsulate the task
    • Use FutureTask to encapsulate Callable objects into thread task objects
  • Hand over the thread task object to Thread for processing
  • Call start() to execute the task
  • by calling

(3) Implementation example

public class CallableDemo {
    
    
    public static void main(String[] args) {
    
    
        /*获取线程任务类对象*/
        Callable<String> myCallable = new MyCallable(10);
        Callable<String> myCallable1 = new MyCallable(20);

        /*FutureTask 封装现成任务类对象*/
        FutureTask<String> f1 = new FutureTask<>(myCallable);
        FutureTask<String> f2 = new FutureTask<>(myCallable1);

        /*交给Thread执行*/
        Thread t = new Thread(f1);
        Thread t1 = new Thread(f2);

        t.start();
        t1.start();

        /*FutureTask 调用get方法获取返回值*/
        try {
    
    
            System.out.println(f1.get());
            System.out.println(f2.get());
        } catch (InterruptedException | ExecutionException e) {
    
    
            throw new RuntimeException(e);
        }
    }
}

class MyCallable implements Callable<String>{
    
    

    private int num;

    public MyCallable() {
    
    
    }

    public MyCallable(int num) {
    
    
        this.num = num;
    }

    @Override
    public String call() throws Exception {
    
    
        int sum = 0;
        for (int i = 1; i < num; i++) {
    
    
            sum += i;
        }
        return "子线程执行结果为 " + sum;
    }
}

/*打印输出*/
子线程执行结果为 45
子线程执行结果为 190

Three, Thread common API

1. Question: How to distinguish threads when there are multiple threads executing at the same time?

method illustrate
String getName() Get the name of the current thread
String setName() Rename the name of the current thread
Thread currentThread() Get the current thread object

2. Implementation example

  public class MultiThreadDemo {
    
    
    public static void main(String[] args) {
    
    
        /*创建线程*/
        Thread t1 = new Thread(()->{
    
    
            for (int i = 0; i < 10; i++) {
    
    
                System.out.println(Thread.currentThread().getName()+"输出:"+i);
            }
        });
        Thread t2 = new Thread(()->{
    
    
            for (int i = 0; i < 10; i++) {
    
    
                System.out.println(Thread.currentThread().getName()+"输出:"+i);
            }
        });
        /*重命名线程*/
        t1.setName("子线程1");
        t2.setName("子线程2");
        /*启动线程*/
        t1.start();
        t2.start();
    }
}

/*打印输出*/
子线程1输出:0
子线程2输出:0
子线程1输出:1
子线程2输出:1
子线程1输出:2
子线程2输出:2
子线程1输出:3
子线程2输出:3
子线程1输出:4
子线程2输出:4
子线程1输出:5
子线程2输出:5
子线程1输出:6
子线程2输出:6
子线程1输出:7
子线程2输出:7
子线程1输出:8
子线程2输出:8
子线程1输出:9
子线程2输出:9

3. The scheme of modifying the thread name - parent class constructor

public class MultiThreadDemo {
    
    
    public static void main(String[] args) {
    
    
        OThread o1 = new OThread("子线程1");
        OThread o2 = new OThread("子线程2");
        o1.start();
        o2.start();
    }
}

class OThread extends Thread{
    
    
    public OThread(){
    
    }

    public OThread(String name){
    
    
        super(name);
    }

    @Override
    public void run() {
    
    
        for (int i = 0; i < 10; i++) {
    
    
            System.out.println(Thread.currentThread().getName()+"执行输出:" + i);
        }
    }
}

/*打印输出*/
子线程1执行输出:0
子线程2执行输出:0
子线程1执行输出:1
子线程2执行输出:1
子线程1执行输出:2
子线程2执行输出:2
子线程1执行输出:3
子线程2执行输出:3
子线程1执行输出:4
子线程2执行输出:4
子线程1执行输出:5
子线程2执行输出:5
子线程1执行输出:6
子线程2执行输出:6
子线程1执行输出:7
子线程2执行输出:7
子线程1执行输出:8
子线程2执行输出:8
子线程1执行输出:9
子线程2执行输出:9

4. Thread sleep of the Thread class

method illustrate
static void sleep(long time) Let the current thread sleep for a specified time before continuing to execute, the unit is milliseconds

5. Thread constructor

constructor illustrate
Thread(String name) assign a name to the current thread
Thread(Runnable target) Pass the Runnable object to the thread object
Thread(Runnable target , String name) Give the Runnable object to the thread object and specify a name for the current thread

4. Thread Safety

1. Related issues

Question 1: What is the thread safety issue?

Answer: Business security issues may arise when multiple threads operate on a shared data at the same time, which is called thread security issues.

Question 2: What is the cause of the thread safety problem?

Answer: Under multi-thread concurrency, multiple threads access and modify a shared data at the same time, which will result in multiple modifications of the shared data.

example

public class ThreadSafeDemo {
    
    

    /*创建一个共享账号,并存入10W元*/
    public static Account account = new Account("ZGYH-1311",100000);

    public static void main(String[] args) {
    
    
        CardThread user1 = new CardThread(account,"小明");
        CardThread user2 = new CardThread(account,"小红");

        user1.start();
        user2.start();
    }
}

class Account{
    
    
    private String CARD;
    private double money;

    public Account() {
    
    
    }

    public Account(String CARD, double money) {
    
    
        this.CARD = CARD;
        this.money = money;
    }

    public String getCARD() {
    
    
        return CARD;
    }

    public void setCARD(String CARD) {
    
    
        this.CARD = CARD;
    }

    public double getMoney() {
    
    
        return money;
    }

    public void setMoney(double money) {
    
    
        this.money = money;
    }

    public void drawnMoney(double money){
    
    
        if (money <= this.money){
    
    
            this.money -= money;
            System.out.println(Thread.currentThread().getName()+"成功取出"+money+"元");
        }else {
    
    
            System.out.println("余额不足,无法取出");
        }
    }
}

class CardThread extends Thread{
    
    
    private Account account;

    public CardThread(Account account,String name) {
    
    
        super(name);
        this.account = account;
    }

    @Override
    public void run() {
    
    
        account.drawnMoney(100000);
    }
}

/*打印输出*/
余额不足,无法取出
小明成功取出100000.0

2. Solution: thread synchronization


Five, thread synchronization

1. The concept of thread synchronization: multiple threads perform the same operation synchronously.

2. The core idea of ​​thread synchronization

  • Locking: Lock the shared resource, only one thread is allowed to access each time, and other threads can only access it after the current thread accesses it.

3. Implementation methods of synchronization
(1) Method 1: Synchronize code blocks

  • Function: Lock the core code that may have thread safety issues.
  • Principle: Only one thread can enter at a time, and it will be automatically unlocked after execution, and other threads can enter and execute.
  • Format
synchronized(同步锁对象){
    
    
	操作共享资源的代码(核心代码)
}
  • Lock object requirements:

In theory, as long as the lock object is the same object for the currently executing thread at the same time, it will also have an impact on other unrelated threads, so there are specifications for lock objects.

Specifications
a. It is recommended to use shared resources as lock objects
b. For instance methods, it is recommended to use the this keyword
c. For static methods, it is recommended to use .class objects as lock objects

example

/*以上面的例子,改变为是将核心代码加同步锁*/
    public void drawnMoney(double money){
    
    
        synchronized (this){
    
    
            if (money <= this.money){
    
    
                this.money -= money;
                System.out.println(Thread.currentThread().getName()+"成功取出"+money+"元");
            }else {
    
    
                System.out.println("余额不足,无法取出");
            }
        }
    }

/*打印输出*/
小明成功取出100000.0
余额不足,无法取出

/*静态方法*/
    public static void print(String message){
    
    
        synchronized (Account.class){
    
    
            System.out.println(message);
        }
    }

(2) Synchronization method
The method of use is the same as that of the synchronization code block.

  • Function: Use the synchronized keyword to lock the method that has a thread safety problem.
  • Format
修饰符 synchronized 返回值类型 方法名称(形参列表){
    
    
	操作代码....
}

example

	public synchronized void drawnMoney(double money){
    
    
           if (money <= this.money){
    
    
               this.money -= money;
               System.out.println(Thread.currentThread().getName()+"成功取出"+money+"元");
           }else {
    
    
               System.out.println("余额不足,无法取出");
           }
   }
  • underlying principle
    • In fact, the bottom layer of the synchronization method also has an implicit lock object. If the lock method is an instance method, this is used as the lock object by default. If the method is a static method, .class is used as the lock object by default.

Related questions

Question 1: Synchronized code blocks compared to synchronized methods

Answer: The synchronization code block lock range is small, and the synchronization method lock range is large

(3) Lock lock

  • Concept: In order to express more clearly how to lock and release locks, JDK5 will provide a new lock object Lock later , and the Lock implementation provides more extensive locking operations than using synchronized methods and statements.

  • Object Type: Interface

  • How to create:

constructor illustrate
ReentrantLock() Obtain Lock lock implementation class
  • Common APIs
method illustrate
void lock() acquire lock
void unlock() release lock

Instructions

  • Declare a constant Lock in the object class
  • Use lock(), unlock() at the core code
class Account {
    
    
    private String CARD;
    private double money;

    private final Lock lock = new ReentrantLock();

    public Account() {
    
    
    }

    public Account(String CARD, double money) {
    
    
        this.CARD = CARD;
        this.money = money;
    }

    public void drawnMoney(double money) {
    
    
        lock.lock();
        /*为避免因为异常导致死锁,所以要捕捉异常并解锁*/
        try {
    
    
            if (money <= this.money) {
    
    
                this.money -= money;
                System.out.println(Thread.currentThread().getName() + "成功取出" + money + "元");
            } else {
    
    
                System.out.println("余额不足,无法取出");
            }
        }catch (Exception e){
    
    
            e.printStackTrace();
        }finally {
    
    
            lock.unlock();
        }
    }

    public static void print(String message) {
    
    
        synchronized (Account.class) {
    
    
            System.out.println(message);
        }
    }
}

6. Thread communication

1. Concept: Threads send data to each other, usually through a shared data communication.

2. Common communication models

  • Producer and consumer model: The producer thread is responsible for producing data, and the consumer thread is responsible for consuming data
    Implementation method: After the producer thread produces data, wake up the consumer, and then wait for itself; after the consumer consumes the data, wake up the producer, and then wait for yourself.
    wake up and wait
method illustrate
void wait() The current thread is waiting to wake up
void notify() The current thread is waiting to wake up
void notifyAll() The current thread is waiting to wake up

Implementation example

public class ThreadCommunication {
    
    

    public static Card account = new Card("ZGYH-1311",0);

    public static void main(String[] args) {
    
    
        /*创建爹对象,生产者*/
        Saver saver1 = new Saver(account,"亲爹");
        Saver saver2 = new Saver(account,"干爹");
        Saver saver3 = new Saver(account,"岳父");

        /*创建子辈对象,消费者*/
        Customer customer1 = new Customer(account,"小明");
        Customer customer2 = new Customer(account,"小红");

        saver1.start();
        saver2.start();
        saver3.start();
        customer1.start();
        customer2.start();

    }
}

class Card{
    
    
    private String card;
    private double money;

    private final Lock lock = new ReentrantLock();

    public Card(){
    
    }

    public Card(String card,double money){
    
    
        this.card = card;
        this.money = money;
    }

    public String getCard() {
    
    
        return card;
    }

    public double getMoney() {
    
    
        return money;
    }

    public void setMoney(double money) {
    
    
        this.money = money;
    }

    public synchronized void save(double money){
    
    
        try {
    
    
            if (this.money == 0 ){
    
    
                this.money += money;
                System.out.println(Thread.currentThread().getName()+"已向"+this.getCard()+"存入金额"+money+"元,剩余金额:"+this.getMoney());
            }
            this.notifyAll();
            this.wait();
        }catch (Exception e){
    
    
            e.printStackTrace();
        }
    }

    public synchronized void get(double money){
    
    
        this.notify();
        try {
    
    
            if (this.money != 0){
    
    
                this.money -= money;
                System.out.println(Thread.currentThread().getName()+"取出金额"+money+",剩余金额:"+this.getMoney());
            }
            this.notifyAll();
            this.wait();
        }catch (Exception e){
    
    
            e.printStackTrace();
        }
    }
}

class Saver extends Thread{
    
    
    private Card account;

    public Saver(){
    
    }

    public Saver(Card account,String name) {
    
    
        super(name);
        this.account = account;
    }

    @Override
    public void run() {
    
    
        while (true){
    
    
            account.save(100000);
            try {
    
    
                sleep(1000);
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
        }
    }
}

class Customer extends Thread{
    
    
    private Card account;

    public Customer(){
    
    }

    public Customer(Card account,String name) {
    
    
        super(name);
        this.account = account;
    }

    @Override
    public void run() {
    
    
        while (true){
    
    
            account.get(100000);
            try {
    
    
                sleep(1000);
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
        }
    }
}

/*打印输出*/
亲爹已向ZGYH-1311存入金额100000.0元,剩余金额:100000.0
小明取出金额100000.0,剩余金额:0.0
岳父已向ZGYH-1311存入金额100000.0元,剩余金额:100000.0
小红取出金额100000.0,剩余金额:0.0
岳父已向ZGYH-1311存入金额100000.0元,剩余金额:100000.0
小明取出金额100000.0,剩余金额:0.0
岳父已向ZGYH-1311存入金额100000.0元,剩余金额:100000.0
小红取出金额100000.0,剩余金额:0.0
干爹已向ZGYH-1311存入金额100000.0元,剩余金额:100000.0
小明取出金额100000.0,剩余金额:0.0
岳父已向ZGYH-1311存入金额100000.0元,剩余金额:100000.0
小红取出金额100000.0,剩余金额:0.0
亲爹已向ZGYH-1311存入金额100000.0元,剩余金额:100000.0
小明取出金额100000.0,剩余金额:0.0
岳父已向ZGYH-1311存入金额100000.0元,剩余金额:100000.0
小红取出金额100000.0,剩余金额:0.0


Seven, thread pool

1. The concept of thread pool: thread pool is a technology that can reuse threads.
2. The reason for using the thread pool: every time the user initiates a request, a new thread is created in the background. When the next new task request is made, a new county will be created, and the creation of a new thread is extremely expensive for system resources.
3. The working principle of the thread pool:
(1) The thread pool consists of two parts: the worker thread WorkThread and the task queue WorkQueue
(2) New task requests will be added to the task queue, and the worker thread is composed of multiple threads. Each thread After the task is completed, the request will be called from the task queue for processing.
4. Thread pool API
(1) Interface: ExecutorService
(2) Creation method:

  • Method 1: Create a thread pool object using the implementation class ThreadPoolExecutor of ExecutorService
public ThreadPoolExecutor(int corePoolSize, 	//线程池的线程数量(核心线程)
                              int maximumPoolSize,	//线程池支持的最大线程数
                              long keepAliveTime,	//临时线程的最大存货时间
                              TimeUnit unit,	//存活时间的单位(时、分、秒、天)
                              BlockingQueue<Runnable> workQueue,	//指定任务队列
                              ThreadFactory threadFactory,	//制定线程工厂
                              RejectedExecutionHandler handler)	//指定线程忙,任务满时,新任务请求的处理

New Task Denial Policy

Strategy illustrate
ThreadPoolExecutor.AbortPolicy Discard and throw RejectedExecutionException (default)
ThreadPoolExecutor.DiscardPolicy Drop the task directly
ThreadPoolExecutor.DiscardOldestPolicy Discard the longest-waiting task in the queue, and Ren Han adds the current task to the queue
ThreadPoolExecutor.CallerRunsPolicy The main thread is responsible for invoking the run() method of the task, thus bypassing the thread pool for direct execution

Question 1: When are temporary threads created?

Answer: When a new task is submitted, it is found that the core threads are busy, the task queue is full, and temporary threads can be created, then temporary threads will be created.

Question 2: When do you start rejecting tasks?

Answer: Both the core thread and the temporary thread are busy, the task queue is full, and the task is rejected when a new task comes.

(3) Common methods of ExecutorService

method illustrate
void execute(Runnable command) Execute tasks, no return value, generally used to execute Runnable tasks
void execute(Callable<T> task) Execute the task, return the future task object to obtain the thread result, generally used to execute the Callable task
Future submit(Runnable command) Runnable tasks are added to the task queue
Future submit(Callable<T> task) Callable tasks are added to the task queue
void shutdown() Close the thread pool after the task is executed
List<Runnable> shutdownNow()() like close, stop the task being executed, and return the unexecuted task in the queue

(4) Thread pool processes Runnable tasks

  • Create a thread pool object
/*创建含有3个核心线程,5个最大线程数,8秒线程存活时间,6个容量的任务队列,默认线程工厂,丢弃任务并抛出异常的线程池*/
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,5,8, TimeUnit.SECONDS,new ArrayBlockingQueue<>(6), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

(5) Thread pool handles Callable tasks

public class ThreadPoolDemo {
    
    
    public static void main(String[] args) {
    
    
        /*创建含有3个核心线程,5个最大线程数,8秒线程存活时间,6个容量的任务队列,默认线程工厂,拒绝任务的线程池*/
        ExecutorService executorService = new ThreadPoolExecutor(3,5,5, TimeUnit.SECONDS,new ArrayBlockingQueue<>(6), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

        Runnable runnable = new newRunnable();

        /*模拟10个任务进入线程池*/
        for (int i = 0; i < 10; i++) {
    
    
            executorService.execute(runnable);
        }

        executorService.shutdown();
    }
}

class newRunnable implements Runnable{
    
    

    @Override
    public void run() {
    
    
        for (int i = 0; i < 5; i++) {
    
    
            System.out.println(Thread.currentThread().getName() + "输出:" + i + "时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(System.currentTimeMillis()));
        }
    }
}

/*打印输出*/
pool-1-thread-3输出:0时间:2023-06-01 12:00:59.783
pool-1-thread-1输出:0时间:2023-06-01 12:00:59.783
pool-1-thread-2输出:0时间:2023-06-01 12:00:59.783
pool-1-thread-4输出:0时间:2023-06-01 12:00:59.783
pool-1-thread-3输出:1时间:2023-06-01 12:00:59.818
pool-1-thread-2输出:1时间:2023-06-01 12:00:59.818
pool-1-thread-1输出:1时间:2023-06-01 12:00:59.818
pool-1-thread-4输出:1时间:2023-06-01 12:00:59.818
pool-1-thread-3输出:2时间:2023-06-01 12:00:59.818
pool-1-thread-2输出:2时间:2023-06-01 12:00:59.818
pool-1-thread-1输出:2时间:2023-06-01 12:00:59.818
pool-1-thread-4输出:2时间:2023-06-01 12:00:59.818
pool-1-thread-3输出:3时间:2023-06-01 12:00:59.818
pool-1-thread-2输出:3时间:2023-06-01 12:00:59.818
pool-1-thread-4输出:3时间:2023-06-01 12:00:59.818
pool-1-thread-1输出:3时间:2023-06-01 12:00:59.818
pool-1-thread-3输出:4时间:2023-06-01 12:00:59.818
pool-1-thread-4输出:4时间:2023-06-01 12:00:59.819
pool-1-thread-1输出:4时间:2023-06-01 12:00:59.819
pool-1-thread-2输出:4时间:2023-06-01 12:00:59.818
pool-1-thread-4输出:0时间:2023-06-01 12:00:59.819
pool-1-thread-1输出:0时间:2023-06-01 12:00:59.819
pool-1-thread-4输出:1时间:2023-06-01 12:00:59.819
pool-1-thread-1输出:1时间:2023-06-01 12:00:59.819
pool-1-thread-2输出:0时间:2023-06-01 12:00:59.819
pool-1-thread-4输出:2时间:2023-06-01 12:00:59.819
pool-1-thread-1输出:2时间:2023-06-01 12:00:59.819
pool-1-thread-3输出:0时间:2023-06-01 12:00:59.819
pool-1-thread-1输出:3时间:2023-06-01 12:00:59.819
pool-1-thread-2输出:1时间:2023-06-01 12:00:59.819
pool-1-thread-4输出:3时间:2023-06-01 12:00:59.819
pool-1-thread-1输出:4时间:2023-06-01 12:00:59.819
pool-1-thread-3输出:1时间:2023-06-01 12:00:59.819
pool-1-thread-1输出:0时间:2023-06-01 12:00:59.820
pool-1-thread-4输出:4时间:2023-06-01 12:00:59.820
pool-1-thread-2输出:2时间:2023-06-01 12:00:59.819
pool-1-thread-3输出:2时间:2023-06-01 12:00:59.820
pool-1-thread-1输出:1时间:2023-06-01 12:00:59.820
pool-1-thread-4输出:0时间:2023-06-01 12:00:59.820
pool-1-thread-2输出:3时间:2023-06-01 12:00:59.820
pool-1-thread-3输出:3时间:2023-06-01 12:00:59.820
pool-1-thread-1输出:2时间:2023-06-01 12:00:59.820
pool-1-thread-2输出:4时间:2023-06-01 12:00:59.820
pool-1-thread-4输出:1时间:2023-06-01 12:00:59.820
pool-1-thread-3输出:4时间:2023-06-01 12:00:59.820
pool-1-thread-1输出:3时间:2023-06-01 12:00:59.820
pool-1-thread-4输出:2时间:2023-06-01 12:00:59.820
pool-1-thread-1输出:4时间:2023-06-01 12:00:59.820
pool-1-thread-4输出:3时间:2023-06-01 12:00:59.820
pool-1-thread-4输出:4时间:2023-06-01 12:00:59.821
  • Method 2: Use Executors (thread pool tool class) to call methods to return thread pool objects with different characteristics

Common APIs

method illustrate
static ExecutorService newCachedThreadPool() The number of threads increases with the increase of tasks. If the thread tasks are executed and idle for a period of time, the threads will be recycled
static ExecutorService newFixedThreadPool(int nThreads) 创建固定数量的线程池,如果某个线程执行发生异常而结束,则线程池补充新线程替代
static ExecutorService newSingleThreadExecutor() 相当于 newFixedThreadPool(1)
static ScheduledExecutorService newScheduledThreadPool(int corePoolSite) 创建线程池,可以实现给定延迟后运行任务或定期完成任务

Excutors底层原理: 基于线程池实现类ThreadPoolExecutor创建线程池对象。

使用示例

**任务类 newRunnable **

class newRunnable implements Runnable{
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 5; i++) {
    
    
            System.out.println(Thread.currentThread().getName() + "输出:" + i + "时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(System.currentTimeMillis()));
        }
    }
}

newCacheThreadPool()

public class ExecutorsDemo {
    
    
    public static void main(String[] args) {
    
    
        Runnable runnable = new newRunnable();
        System.out.println("——————————newCacheThreadPool——————————");
        /*线程数随着任务数量的变化而变化*/
        ExecutorService e = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
    
    
            e.submit(runnable);
        }
        e.shutdown();
    }
}

/*打印输出*/
——————————newCacheThreadPool——————————
pool-1-thread-2输出:0时间:2023-06-01 13:55:43.601
pool-1-thread-5输出:0时间:2023-06-01 13:55:43.601
pool-1-thread-4输出:0时间:2023-06-01 13:55:43.601
pool-1-thread-2输出:1时间:2023-06-01 13:55:43.638
pool-1-thread-3输出:0时间:2023-06-01 13:55:43.601
pool-1-thread-1输出:0时间:2023-06-01 13:55:43.601
pool-1-thread-2输出:2时间:2023-06-01 13:55:43.638
pool-1-thread-4输出:1时间:2023-06-01 13:55:43.638
pool-1-thread-3输出:1时间:2023-06-01 13:55:43.638
pool-1-thread-5输出:1时间:2023-06-01 13:55:43.638
pool-1-thread-2输出:3时间:2023-06-01 13:55:43.638
pool-1-thread-1输出:1时间:2023-06-01 13:55:43.638
pool-1-thread-3输出:2时间:2023-06-01 13:55:43.639
pool-1-thread-4输出:2时间:2023-06-01 13:55:43.639
pool-1-thread-2输出:4时间:2023-06-01 13:55:43.639
pool-1-thread-5输出:2时间:2023-06-01 13:55:43.639
pool-1-thread-1输出:2时间:2023-06-01 13:55:43.639
pool-1-thread-4输出:3时间:2023-06-01 13:55:43.639
pool-1-thread-3输出:3时间:2023-06-01 13:55:43.639
pool-1-thread-5输出:3时间:2023-06-01 13:55:43.639
pool-1-thread-1输出:3时间:2023-06-01 13:55:43.639
pool-1-thread-4输出:4时间:2023-06-01 13:55:43.639
pool-1-thread-3输出:4时间:2023-06-01 13:55:43.639
pool-1-thread-5输出:4时间:2023-06-01 13:55:43.639
pool-1-thread-1输出:4时间:2023-06-01 13:55:43.639

newFixedThreadPool

public class ExecutorsDemo {
    
    
    System.out.println("——————————newFixedThreadPool——————————");
	ExecutorService e2 = Executors.newFixedThreadPool(2);
	for (int i = 0; i < 5; i++) {
    
    
           e2.submit(runnable);
        }

        e2.shutdown();
}

/*打印输出*/
——————————newFixedThreadPool——————————
pool-1-thread-1输出:0时间:2023-06-01 14:17:21.888
pool-1-thread-2输出:0时间:2023-06-01 14:17:21.888
pool-1-thread-1输出:1时间:2023-06-01 14:17:21.919
pool-1-thread-2输出:1时间:2023-06-01 14:17:21.919
pool-1-thread-1输出:2时间:2023-06-01 14:17:21.920
pool-1-thread-2输出:2时间:2023-06-01 14:17:21.920
pool-1-thread-1输出:3时间:2023-06-01 14:17:21.920
pool-1-thread-2输出:3时间:2023-06-01 14:17:21.920
pool-1-thread-1输出:4时间:2023-06-01 14:17:21.920
pool-1-thread-2输出:4时间:2023-06-01 14:17:21.920
pool-1-thread-1输出:0时间:2023-06-01 14:17:21.920
pool-1-thread-1输出:1时间:2023-06-01 14:17:21.920
pool-1-thread-1输出:2时间:2023-06-01 14:17:21.920
pool-1-thread-1输出:3时间:2023-06-01 14:17:21.921
pool-1-thread-2输出:0时间:2023-06-01 14:17:21.921
pool-1-thread-1输出:4时间:2023-06-01 14:17:21.921
pool-1-thread-2输出:1时间:2023-06-01 14:17:21.921
pool-1-thread-1输出:0时间:2023-06-01 14:17:21.921
pool-1-thread-2输出:2时间:2023-06-01 14:17:21.921
pool-1-thread-1输出:1时间:2023-06-01 14:17:21.921
pool-1-thread-2输出:3时间:2023-06-01 14:17:21.921
pool-1-thread-1输出:2时间:2023-06-01 14:17:21.921
pool-1-thread-2输出:4时间:2023-06-01 14:17:21.921
pool-1-thread-1输出:3时间:2023-06-01 14:17:21.921
pool-1-thread-1输出:4时间:2023-06-01 14:17:21.921

newSingleThreadExecutor

public class ExecutorsDemo {
    
    
        System.out.println("——————————newSingleThreadExecutor——————————");
        ExecutorService e3 = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 5; i++) {
    
    
            e3.submit(runnable);
        }

        e3.shutdown();
}

/*打印输出*/
——————————newSingleThreadExecutor——————————
pool-1-thread-1输出:0时间:2023-06-01 14:36:17.093
pool-1-thread-1输出:1时间:2023-06-01 14:36:17.120
pool-1-thread-1输出:2时间:2023-06-01 14:36:17.121
pool-1-thread-1输出:3时间:2023-06-01 14:36:17.121
pool-1-thread-1输出:4时间:2023-06-01 14:36:17.121
pool-1-thread-1输出:0时间:2023-06-01 14:36:17.121
pool-1-thread-1输出:1时间:2023-06-01 14:36:17.121
pool-1-thread-1输出:2时间:2023-06-01 14:36:17.121
pool-1-thread-1输出:3时间:2023-06-01 14:36:17.122
pool-1-thread-1输出:4时间:2023-06-01 14:36:17.122
pool-1-thread-1输出:0时间:2023-06-01 14:36:17.122
pool-1-thread-1输出:1时间:2023-06-01 14:36:17.122
pool-1-thread-1输出:2时间:2023-06-01 14:36:17.122
pool-1-thread-1输出:3时间:2023-06-01 14:36:17.122
pool-1-thread-1输出:4时间:2023-06-01 14:36:17.122
pool-1-thread-1输出:0时间:2023-06-01 14:36:17.122
pool-1-thread-1输出:1时间:2023-06-01 14:36:17.122
pool-1-thread-1输出:2时间:2023-06-01 14:36:17.123
pool-1-thread-1输出:3时间:2023-06-01 14:36:17.123
pool-1-thread-1输出:4时间:2023-06-01 14:36:17.123
pool-1-thread-1输出:0时间:2023-06-01 14:36:17.123
pool-1-thread-1输出:1时间:2023-06-01 14:36:17.123
pool-1-thread-1输出:2时间:2023-06-01 14:36:17.123
pool-1-thread-1输出:3时间:2023-06-01 14:36:17.123
pool-1-thread-1输出:4时间:2023-06-01 14:36:17.123

newScheduledThreadPool

public class ExecutorsDemo {
    
    
        System.out.println("——————————newScheduledThreadPool——————————");
        ExecutorService e4 = Executors.newScheduledThreadPool(20);
        for (int i = 0; i < 5; i++) {
    
    
            e4.submit(new Runnable() {
    
    
                @Override
                public void run() {
    
    
                    System.out.println(Thread.currentThread().getName()+"执行了");
                }
            });
        }

        e4.shutdown();
}

/*打印输出*/
——————————newScheduledThreadPool——————————
pool-1-thread-1执行了
pool-1-thread-3执行了
pool-1-thread-2执行了
pool-1-thread-4执行了
pool-1-thread-5执行了

使用Executors工具类可能产生的问题

  • 固定线程池与单例线程池产生的问题: 允许请求任务队列长度为Integer.MAX_VALUE,可能出现OOM错误(内存溢出)
  • 缓冲线程池与计划线程池产生的问题: 创建的线程数量为Integer.MAX_VALUE,线程数量随任务数量增加,可能会出现OOM错误。

八、定时器

1、概念: 是一种控制任务延时调用或周期调用的技术。

2、作用: 定时执行任务

3、实现方式
(1)Timer

构造器 说明
Timer() 创建计时器对象
方法 说明
void schedule(TimerTask task,long delay,long period) 创建带有任务,延时时间,间隔周期参数的执行方法

使用示例

public class TimerDemo {
    
    
    public static void main(String[] args) {
    
    
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
    
    
            @Override
            public void run() {
    
    
                System.out.println(Thread.currentThread().getName()+"执行了,执行时间:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(System.currentTimeMillis()));
            }
        },1000,2000); //延时1s开始,以2s为一个执行周期
    }
}

/*打印输出*/
Timer-0执行了,执行时间:2023-06-01 15:49:56.834
Timer-0执行了,执行时间:2023-06-01 15:49:58.812
Timer-0执行了,执行时间:2023-06-01 15:50:00.818

存在的问题

  • Timer是单线程,当有多个任务执行时,存在延时和设置定时器的时间有差异。
  • 可能因为某个任务异常而终止,影响后续执行。

(2)ScheduleExecutorService

  • 优点: 基于线程池,某个任务执行情况不会影响到其它线程。
方法 说明
static ScheduledExecutorService newScheduledTreadPool(int corePoolSize) 得到线程池对象
ScheduledFuture<?> scheduleAtFixedRate(Rinnable command,long delay,long period, TimeUnit unit) 创建周期调度方法

使用示例


public class ScheduleDemo {
    
    
    public static void main(String[] args) {
    
    
        ScheduledExecutorService service = Executors.newScheduledThreadPool(3);
        service.scheduleAtFixedRate(() -> {
    
    
            System.out.println(Thread.currentThread().getName()+"执行了,时间:"+new Date());
        },0,1, TimeUnit.SECONDS);
    }
}

九、补充知识
1、并发: 因CPU同时处理的线程有限,CPU会轮询每一个线程服务,由于CPU切换速度极快,因此我们会认为这些线程都是同时执行的,这就叫并发。
2、并行: CPU可多线程执行的能力叫并发。
3、生命周期: 是线程从创建到销毁的过程。
(1)六种状态

  • NEW——新建状态
  • RUNNABLE——运行状态
  • BLOCKED——锁阻塞状态
  • WATTING——等待状态
  • TIMED_WATING——计时等待状态
  • TERMINATED——终止死亡状态
    在这里插入图片描述

Guess you like

Origin blog.csdn.net/Zain_horse/article/details/130969982