[JavaEE] Multi-threaded code example: timer and thread pool

Table of contents

timer:

What is a timer? 

Timers in the standard library: 

Implement the timer: 

​Thread pool: 

What is a thread pool? 

The thread pool in the standard library: 

Factory pattern: 

Implementation of the thread pool: 


timer:

What is a timer? 

Timers are also an important component in software development. Similar to an "alarm clock".. After a set time is reached, a specified code is executed. This is very similar to the alarm clock we usually talk about, but it can not only serve as a reminder, but also actually do it. And there are still many uses of timers in network programming. For example: when we visit a webpage, we set a timer. Once the visit time is too long, the timer will be triggered, and the visit will end, and the connection will be disconnected in time, without blocking and waiting.

Timers in the standard library: 

A Timer class is provided in the standard library, and we can use this Timer to do what we want to do regularly.

The core method of the Timer class is schedule, which contains two parameters. The first parameter specifies the task code to be executed, and the second parameter specifies how long to execute the task (in ms)

import java.util.Timer;
import java.util.TimerTask;

public class ThreadDemo1 {
    public static void main(String[] args) {
        //设置定时器
        Timer timer=new Timer();
        System.out.println("已经设置好定时器!!!");
        //指定定时器的任务和执行时间
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("执行任务1!!");
            }
        },1000);

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("执行任务2!!");
            }
        },2000);

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("执行任务3!!");
            }
        },3000);
    }
}

Implement the timer: 

//实现一个定时器

import java.util.concurrent.PriorityBlockingQueue;

/**
 *  实现定时器的核心:
 *  1、定时器需要让任务有一个放置的地方(考虑使用优先级阻塞队列,可以实现线程安全和优先级的情况)
 *  2、定时器需要让任务在指定的时间进行执行(考虑使用一个线程不断进行扫描来进行判断)
 */
//任务
class MyRunnable implements Comparable<MyRunnable>{
    //任务使用Runnable创建
    private Runnable runnable;
    //推迟时间(单位:ms)
    private long time;
    //构造方法
    public MyRunnable(Runnable runnable,long time){
        this.runnable=runnable;
        this.time=time;
    }

    public Runnable getRunnable() {
        return runnable;
    }

    public long getTime() {
        return time;
    }
    //执行任务
    public void run(){
        runnable.run();
    }
    //重写

    @Override
    public int compareTo(MyRunnable o) {
        return (int)(this.time-o.time);
    }
}

class MyTimer{
    //使用object来控制阻塞队列执行顺序
    private Object loker=new Object();
    //需要一个优先级阻塞队列
    PriorityBlockingQueue<MyRunnable>queue=new PriorityBlockingQueue<>();
    //一个线程用来扫描队列中的任务(需要在构造方法中实现)
    public MyTimer(){
        Thread t=new Thread(()->{
            while(true){
                try {
                    synchronized (loker){
                        MyRunnable task=queue.take();
                        //记录当前的时间戳
                        long curTime=System.currentTimeMillis();
                        //判断是否到达执行时间
                        //如果没有到达执行时间就把任务再放回阻塞队列当中
                        if(curTime< task.getTime()){
                            queue.put(task);
                            //进行阻塞
                            loker.wait(task.getTime()-curTime);
                            //如果到达执行时间则直接执行任务
                        }else{
                            task.run();
                        }
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }
        });
        t.start();

    }
    public void schedule(Runnable runnable,long after){
        MyRunnable runnable1=new MyRunnable(runnable,System.currentTimeMillis()+after);
        queue.put(runnable1);
        synchronized (loker){
            loker.notify();
        }

    }

}


public class MyTask {
    public static void main(String[] args) {
        MyTimer timer1=new MyTimer();
        timer1.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("到达任务1执行时间,执行任务1!");
            }
        },1000);
        MyTimer timer2=new MyTimer();
        timer2.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("到达任务2执行时间,执行任务2!");
            }
        },3000);

    }

}

Thread Pool: 

What is a thread pool? 

When it comes to thread pools, we should be able to think of many similar "pools" we have learned about, such as string constant pools. Although we say that the overhead of creating and destroying threads is smaller than that of processes, it is undeniable that each creation and destruction consumes a lot of resources. (How much is only relatively speaking), in order to make the overhead smaller, we introduced a thread pool.

The biggest benefit of the thread pool is the overhead of starting and destroying threads each time!

Just imagine, there are now 10 threads. Do we create them once and put them in the thread pool to consume less resources for backup, or do they create one by one with less overhead? The answer, of course, is to create 10 at a time, one by one, consuming resources each time, and consuming resources each time they are destroyed, and creating multiple at a time greatly reduces overhead. 

The thread pool in the standard library: 

The thread pool in the standard library uses Executors.newFixedThreadPool(10) to create a thread pool with a fixed number of 10 threads.
The return value type is ExecutorService , and a task can be registered in the thread pool  through ExecutorService.submit .

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

public class ThreadPool1 {
    public static void main(String[] args) {
        //在线程池中创建10个线程
        ExecutorService pool= Executors.newFixedThreadPool(10);
        for (int i = 1; i <=10; i++) {
            int n=i;
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程池中执行任务"+n);
                }
            });

        }
    }
}

 

The static method of the Executors class we use here directly constructs the object without using the new operation. Using the static method of the class to construct the object is equivalent to hiding the new operation in the static method. Then use the submit method to send the task to the thread pool in the form of the Runnable interface. 

Executors are essentially a wrapper around the ThreadPoolExecutor class. 

Factory pattern: 

Here is a design pattern - the factory pattern 

The method that hides the new operation inside the static method is the factory method. A class that provides a factory method is called a factory class. This design pattern is called the factory pattern. What are the benefits of the factory pattern? We know that the design pattern is largely to fill some grammatical pits, and the factory pattern is mainly to fill the pits of the construction method, and does not expose the creation logic to the client when creating objects. For example, when creating a coordinate system, there are two types of Cartesian and polar coordinates. Generally, our parameters are double. At this time, if you want to complete the creation through overloading, you will not succeed. We need to use the construction method.

The above is one of the simple uses, there are many ways to create a thread pool, such as:

 

At the same time, one thing to note is that when our main thread ends, the program does not stop, because the threads in our thread pool belong to the foreground thread, which will prevent our process from ending. At the same time, it should be noted that the tasks in our timer above are also foreground threads. At this time, if we want to stop, we need to click the stop button above.

Implementation of the thread pool: 

1. The core operation is submit, which adds tasks to the thread pool.
2. Use Runnable to describe a task.
3. Use a BlockingQueue to organize all tasks.
4. What each thread has to do: Keep taking tasks from BlockingQueue and executing them.
5. Specify the maximum number of threads n in the thread pool; when the current number of threads exceeds this maximum value, no new threads will be added.

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
class MyThreadPool{
    //阻塞队列当中放入任务
    private BlockingQueue<Runnable> queue=new LinkedBlockingQueue<>();
    //构造方法中创建出工作的线程
    public MyThreadPool(int n){
        for (int i = 0; i < n; i++) {
            //创建线程
            Thread t=new Thread(()->{
                while(true){
                    //任务为空
                    Runnable runnable= null;
                    try {
                        //将阻塞队列中的任务拿出给runnable
                        runnable = queue.take();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    //执行任务
                    runnable.run();
                }
            });
            t.start();
        }
    }

    //把任务放到阻塞队列中(注册任务)
    public void submit(Runnable runnable){
        try {
            queue.put(runnable);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

public class ThreadPool2 {
    public static void main(String[] args) {
        //创建一个带有10个线程的线程池
        MyThreadPool myThreadPool=new MyThreadPool(10);
        for (int i = 0; i <10 ; i++) {
            int n=i+1;
            myThreadPool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("执行线程池中任务:"+n);
                }
            });
        }
    }
}

 

Guess you like

Origin blog.csdn.net/m0_67995737/article/details/129059156