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

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

其他实现方式

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

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

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

时间轮总结

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

定时器运用

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

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

相关推荐
2501_930707784 小时前
使用C#代码在 PowerPoint 中组合或取消组合形状
开发语言·c#
晚烛5 小时前
CANN 调试工具与性能剖析:从日志分析到 NPU 行为追踪的完整调试体系
开发语言·windows·python·深度学习·缓存
惊鸿一博5 小时前
图标加载方式_zeroIcon_是否加前缀mdi
开发语言·前端·javascript
王八八。5 小时前
linux后台java、postSQL部署命令
java·linux·运维
森G5 小时前
TypeScript 基础类型
开发语言·typescript
月落归舟6 小时前
MyBatis缓存机制
java·缓存·mybatis
huipeng9266 小时前
企业级微服务开发实战(一):项目启动与工程化设计
java·开发语言·spring boot·spring cloud·微服务·云原生·架构
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ6 小时前
java实现excel导入、下载模板方法
java·开发语言·excel
眠りたいです6 小时前
现代C++:C++14中的新语言特性和库特性
c语言·开发语言·c++
段ヤシ.7 小时前
回顾Java知识点,面试题汇总Day12(持续更新)
java·mybatis