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 差不多,都是使用一个线程负责扫描队首元素并执的, 如

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

其他实现方式

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

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

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

时间轮总结

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

定时器运用

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

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

相关推荐
程序员清风15 分钟前
专业再升级!程序员专属显示器明基RD280UG上手实测!
java·后端·面试
JJJennie77716 分钟前
Gemini 3.7 Flash 上线,Google 想让 AI 多干活
开发语言·javascript·ecmascript
wuyk5551 小时前
Python 第三章:列表 List
服务器·开发语言·python
Evand J1 小时前
【MATLAB例程,车联网7】CACC网联车辆协同控制:ACC对比、V2X时延丢包与队列稳定性,附例程下载链接
开发语言·matlab·车联网
Lam Tang2 小时前
APS 系列文章 08
java·代理模式
要努力点2 小时前
Python入门第六课
开发语言·python
萧瑟余晖3 小时前
Java深入解析篇三十三之消息队列
java·开发语言
码匠许师傅3 小时前
【C++ 面试真题】聊聊 C++ 的序列容器
java·c++·面试
IT爱学堂3 小时前
尚硅谷Java+AI大模型应用开发革新版本 2025年3月
java·开发语言·人工智能
C++ 老炮儿的技术栈3 小时前
Qt5.9.1 Windows 完整开发环境搭建流程
开发语言·c++·windows·qt·编辑器·代码化