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

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

其他实现方式

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

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

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

时间轮总结

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

定时器运用

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

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

相关推荐
摇滚侠4 小时前
《On Java 中文版 基础卷》阅读笔记 对象无处不在 03
java·笔记·python
二十雨辰4 小时前
[学成在线]-01项目背景
java
小小小小钰儿4 小时前
1.5-Python网络编程
开发语言·网络·python·web安全·计算机·网络安全
hiahiahia1234 小时前
实现完整 Tool Dispatcher
开发语言·前端
AC赳赳老秦5 小时前
公开音频转写信息提取:OpenClaw 处理发布会与听证会文本并提取核心决策信息
大数据·开发语言·汇编·数据库·人工智能·deepseek·openclaw
弈栈录5 小时前
Java AI 应用的异步化与高并发设计
java·后端·架构
@yanyu6665 小时前
C编译器安装与第一个C程序
c语言·开发语言
落魄实习生5 小时前
Agent Scope Java 2.x 系列【2】 ReActAgent
java·开发语言
智鸟科技GemeOpen开发者智能设备5 小时前
智能插座二次开发如何接入Home Assistant,从零开始完整工程实现(Python+React)
开发语言·python·物联网·react.js·智能家居