JavaEE|多线程初阶(七)

线程池知识点总结

ThreadPoolExecutor是Java提供的线程池的类

Executor

创建出固定线程的线程池/自动扩容线程数的线程池

int corePoolSize和int maximumPoolSize

核心线程数和最大线程数

long keepAliveTime和TimeUnit unit

非核心线程允许空闲的最大时间

ThreadFactory threadFactory

接口,实现newThread

RejectedExecutionHandler handler

拒绝策略

线程池的模拟实现

java 复制代码
class MyThreadPool{
    private static BlockingQueue<Runnable> qeque=null;
    public MyThreadPool(int n){
        qeque=new ArrayBlockingQueue<>(n);
        for (int i = 0; i < n; i++) {
            Thread thread = new Thread(() -> {

                try {
                    while (true) {
                        Runnable task = qeque.take();
                        task.run();
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
            thread.start();
        }
    }
    public void submit(Runnable runnable) throws InterruptedException {
        qeque.put(runnable);
    }
}
public class Demo01 {
    public static void main(String[] args) throws InterruptedException {
            MyThreadPool myThreadPool=new MyThreadPool(10);
        for (int i = 0; i < 100; i++) {
            int id=i;
            myThreadPool.submit(()->{
                System.out.println("hello:"+id+","+Thread.currentThread().getName());
            });
        }
    }
}

线程池最核心的就是submit这样的操作,往线程池里添加任务(任务就是runnable),同时得有线

程来执行任务

在构造方法中把线程创建出来,因为随时都有新的任务被添加进来,所以线程就需要持续不断的尝

试执行读取任务,取到了就执行,没取到就阻塞等待

java 复制代码
for (int i = 0; i < n; i++) {
            Thread thread = new Thread(() -> {

                try {
                    while (true) {
                        Runnable task = qeque.take();
                        task.run();
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
            thread.start();

当任务执行完毕之后,我们发现虽然任务已经结束了,但是进程仍然没有结束

对于这个现象是因为线程池里的这些线程,还在take阻塞的(等待),同时线程池里的线程是后台

线程,组织进程结束(可以通过setDaemon()方法设置为前台线程)

进程的退出码

正常退出

异常退出

定时器

定时器类似于闹钟,时间到了,执行一些逻辑

Timer

Java标注库的定时器,正常描述任务是Runnable,在定时器这里稍微特殊一点,把Runnable封装

了一下TimerTask

观察TimerTake这个方法,发现要求实现Runnable接口,核心还是重写run方法

定时器的使用

java 复制代码
 public static void main(String[] args) {
        Timer timer1= new Timer();
        timer1.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello timer,3000");
            }
        },3000);
        Timer timer2= new Timer();
        timer2.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello timer,2000");
            }
        },2000);
        Timer timer3= new Timer();
        timer3.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello timer,1000");
            }
        },1000);
    }

对于new TimerTask(){}是匿名内部类的写法

  1. 创建了子类
  2. 重写了run
  3. new了子类的实例

模拟实现定时器

  1. 创建一个类表示一个任务
  2. 定时器中能够管理多个任务,必须使用一些集合类把这些任务管理起来。当管理多个任务的时候, 需要确保时间最早的任务最先执行. 通过遍历的方式, 找到时间最早.
  3. 实现schedule方法,把任务添加到队列即可
  4. 额外创建一个线程,负责执行队列中的任务。定时器和线程池不同,线程池是只要队列不为空, 就立即取任务并执行.此处需要看队首元素的时间是否到了,时间到才能执行, 时间不到不能执行.

对于我们自己设定的定时器,必须要明确比较规则,可以通过实现compareTo接口

这里需要把自己定义的类传进去

任务在当前时刻为基准,多长时间之后开始执行

获取当前时刻时间的api,返回的是一个long,ms级别的时间戳

循环等待,等到queue中出现新的数据

使用while就是为了"再次确认,条件是否满足",当前wait唤醒之后通过总的while循环刚好就是再走

一遍确认逻辑

这样的操作,创建了一个带有线程池的定时器

具体代码如下

java 复制代码
class MyTimerTask implements Comparable<MyTimerTask> {
    private long time;
    private Runnable task;

    public MyTimerTask(Runnable task, long time) {
        this.time = time;
        this.task = task;
    }

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

    public long getTime() {
        return time;
    }

    public void run() {
        task.run();
    }
}

class MyTimer {
    public static PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();

    public void schedule(Runnable task, long delay) {
        synchronized (this) {
            MyTimerTask timerTask = new MyTimerTask(task, System.currentTimeMillis() + delay);
            queue.offer(timerTask);
            this.notify();
        }
    }

    public MyTimer() {
        Thread t = new Thread(() -> {
            while (true) {
                while (queue.isEmpty()) {
                    try {
                        synchronized (this) {
                            this.wait();
                        }
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                MyTimerTask task = queue.peek();
                if (System.currentTimeMillis() < task.getTime()) {
                    try {
                        synchronized (this) {
                            this.wait(task.getTime() - System.currentTimeMillis());
                        }
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                } else {
                    task.run();
                    queue.poll();
                }
            }
        });
        t.start();
    }
}

public class Demo04 {
    public static void main(String[] args) {
        MyTimer timer = new MyTimer();
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        }, 3000);
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        }, 2000);
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        }, 1000);
        Executors.newScheduledThreadPool(4);
    }
}

标准库提供的 Timer 和 自己写 MyTimer 差不多,都是使用一个线程负责扫描队首元素并执的, 如

果任务少/任务的时间分散都无所谓. 如果任务特别多, 时间非常集中~~ 一个线程就可能执行不过来

其他实现方式

定时器, 除了基于 堆(优先队列) 方式来实现的定时器之外,还有一种方案, 基于 "时间轮"类似搞个循

环队列 (数组),每个元素是一个 "时间单位"而且每个元素又是一个链表,每到一个时间单位, 光标

指向下一个元素, 同时把这个元素上对应链表中的任务都执行一遍

时间轮总结

优势是性能更高,劣势时间精度不如优先队列.

定时器运用

由于定时器, 是一个非常重要的组件, 在分布式系统中, 把定时器专门提取出来, 封装成一个单独的

服务器 (和消息队列是很像)

相关推荐
谭欣辰1 小时前
C++ 排列组合完整指南
开发语言·c++·算法
foundbug9992 小时前
自适应滤除直达波干扰的MATLAB实现
开发语言·算法·matlab
XDH_CS2 小时前
MySQL 8.0 安装与 MySQL Workbench 使用全流程(超详细教程)
开发语言·数据库·mysql
小短腿的代码世界3 小时前
Qt实时盈亏计算深度解析:从持仓数据到动态盈亏展示
开发语言·qt
小康小小涵3 小时前
基于ESP32S3实现无人机RID模块底层源码编译
linux·开发语言·python
lzjava20243 小时前
Python的函数
开发语言·python
掌心向暖RPA自动化4 小时前
如何获取网页某个元素在屏幕可见部分的中心坐标影刀RPA懒加载坐标定位技巧
java·javascript·自动化·rpa·影刀rpa
Awesome Baron4 小时前
skill、tool calling、MCP区别
开发语言·人工智能·python