【Java ee初阶】多线程(7)

一、线程池

线程池的一些参数:

corePoolSize:核心线程数量

maximumPoolSize:核心线程数量+临时线程数量

上述是"java 的线程池策略"(其他语言,其他库的线程池可能不同)

keepAliveTime :临时线程的存活时间.临时线程允许摸鱼的最大时间

Timeunit:时间单位:秒/分钟/毫秒...

workQueue:线程池要完成的任务队列,每个任务都是通过Runnable来进行描述的

线程池在使用的时候,首先会创建一些线程,然后需要调用者给线程池放一些任务,这些线程就会从队列中取出任务,并且执行里面的代码。

(*工厂设计模式:正常创建一个对象是通过构造方法,new出来的。但是构造方法存在一定缺陷的,工厂模式就用来填补上这样的缺陷。不用构造方法来出池化对象,通过一组静态方法来初始化对象,静态方法就可以起不同的名字来做区分了。)

ThreadFactory:线程工厂 这个类就是Thread类的工厂类,由于在线程池中,是需要创建很多线程的,创建出来的线程需要进行哪些初始化操作?就可以通过threadFactory来进行设定。

实际上就是通过newThread方法,针对Thread对象进行初始化,再把这个Thread对象返回出来。比如想把线程池中的线程,按照一定的规则,设置name,或者想把线程池的线程全都设置成后台线程,或者都设置xxx为优先级。

例如,我想要将笛卡尔坐标系下的点转化为极坐标下的点表示,那么------

然而,多个版本的构造方法,需要"重载",而重载要求方法名称相同但是参数列表不能相同,这里的参数列表相同,因此无法构成重载。那么此时的解决方法也就是:

这就是用来构造Point对象的类,也被称为"工厂类"

RejectedExecutionHandler:拒绝策略

线程池,有任务列表、阻塞队列,当任务队列满了的时候,如果再次添加新的任务,会发生什么呢?

正常来说,除非特殊说明,我们写的代码是不希望有这种突发性的阻塞的,因为这种阻塞稍不留神可能就对程序造成不可预估的影响。

因此直接让添加任务的线程阻塞,其实是不太好的,不太好就意味着要有其他的方法------因此标准库的线程池就引入了"拒绝策略"

正常情况下,是线程池里面的线程执行任务的,但是现在线程池里面的线程忙不过来了,就只能由调用者自己找个线程来执行任务

标准库中提供了一个对ThreadPoolExecutor进行了封装的简化版本Executors,本质上也是工厂类,提供了一些工厂方法,对上述的ThreadPoolExecutor进行不同的初始化。

这里的工厂方法,就是在创建不同风格的线程池。

java 复制代码
package Thread;

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

public class demo39 {
    public static void main(String[] args) {
        //固定线程数目的线程池,核心线程数和最大线程数都是4
        ExecutorService executorService = Executors.newFixedThreadPool(4);
        //核心线程数设置为0,最大线程数设为一个Int最大值这样的线程池
        ExecutorService executorService1 = Executors.newCachedThreadPool();
        //固定只有一个线程的线程池(这个东西确实用的不多,可以通过这种方式来代替创建线程的方式)
        ExecutorService executorService2 = Executors.newSingleThreadExecutor();
        //这个线程池,本质上是一个定时器,放到这个线程池中的任务,会在一定时间之后执行
        ExecutorService executorService3 = Executors.newScheduledThreadPool(4);

    }

}

现在我们来让这个线程池来执行一些任务,为了更清晰地看出这个任务做了10次,我们来对i进行打印。然而这里不能直接这么些,会出现"变量捕获"的问题。

这里通过这种方法来进行解决。

有些公司更加想要完全体的ThreadPoolExecutor,可控性更强。

如果队列中没有其他的任务,take就会阻塞,一直阻塞到其他线程执行submit为止

javascript 复制代码
package Thread;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

class MyFixedThreadPool{ // 固定线程池。
    private BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<Runnable>(); // 阻塞队列,用于存放任务。
    public MyFixedThreadPool(int n){ // 构造方法,初始化线程池。
        for(int i = 0; i < n; i++){ // 创建n个线程。
            Thread t = new Thread(()->{ // 创建线程。
            try {
                while (true) {
                    Runnable task = blockingQueue.take(); // 从阻塞队列中取出任务。
                   task.run(); // 执行任务。
                }
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }    
            });
        t.start();    
        }       
}
    public void submit(Runnable task) throws Exception{ // 提交任务。
        // 1. 将任务放入阻塞队列。
        blockingQueue.put(task);
    }
}

public class demo40 {
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        MyFixedThreadPool myFixedThreadPool = new MyFixedThreadPool(100); // 创建线程池。
        for(int i = 0; i < 10; i++){ // 提交1000个任务。
            int finalI = i; // 闭包。
            myFixedThreadPool.submit(()->{ // 提交任务。
                System.out.println("任务" + finalI + "正在执行"); // 打印任务正在执行。
            });
        }
    }

}

二、定时器

定时器在java编程中是一个非常终于熬的组件。

阻塞队列,太重要了,太常用了,在实际工作中,会把阻塞队列单独封装成一个/一组服务器

定时器也是类似,也会被单独封装成一个/一组服务器,类似于闹钟。

为什么需要定时器呢?------编程中有些任务,不需要立即执行,而是要等一会儿,等到到一定时间的时候再去执行。

Java标准库提供了Timer这样的类,可以通过他来实现简单的定时器。

一开始输出:程序启动,过了4s之后输出"定时器执行任务"

自行实现一个定时器

java 复制代码
package Thread;

import java.util.PriorityQueue;



class MyTimerTask implements Comparable<MyTimerTask>{
    private Runnable task;
    private long delay;
    private long time ;

    public MyTimerTask(Runnable task, long delay){
        this.task = task;
        this.time = delay + System.currentTimeMillis();
    }
    public Runnable getTask(){
        return task;
    }
    public long getTime(){
        return time;
    }
    public int compareTo(MyTimerTask o){ // 比较两个任务的时间,谁的时间小谁排在前面。
        return (int)(this.time - o.time); // 比较两个任务的时间,谁的时间小谁排在前面。
    } // 比较两个任务的时间,谁的时间小谁排在前面。
}

//自己实现定时器
//定时器能够同时管理多个任务
//有一定的数据结构来组织这多个任务
//ArrayList不太合适
//更好的选择是优先级队列

class MyTimer{
    private PriorityQueue<MyTimerTask> queue = new PriorityQueue<MyTimerTask>();
    private static Object locker = new Object();
    public MyTimer(){
        //创建线程
        Thread t = new Thread(()->{ // 线程的任务是不断地从队列中取出任务,并执行。
    while (true) {
        try { // 捕获异常。
            synchronized(locker){ // 出队列。
                if(queue.isEmpty()){
                    locker.wait();
                    
                }
                MyTimerTask task = queue.peek(); // 取出队列中的第一个任务。
                long curTime = System.currentTimeMillis(); // 获取当前时间。
                if(curTime < task.getTime()){ // 如果当前时间小于任务的时间,说明任务还没有到执行时间。
                    locker.wait(task.getTime() - curTime);
            }else{
                queue.poll(); // 取出队列中的第一个任务。
                task.getTask().run(); // 执行任务。
            }
            }
        } catch (InterruptedException e) { // 捕获异常。
            e.printStackTrace(); // 打印异常栈。
     }
     }
    });
     t.start(); // 启动线程。
    }

    public void schedule(Runnable task, long delay){ 
        synchronized(locker){ // 入队列。
            queue.offer(new MyTimerTask(task, delay)); // 入队列。
            locker.notify(); // 唤醒线程。
        } // 入队列。

    }
}

public class demo42 {

    public static void main(String[] args) {
        MyTimer timer = new MyTimer(); // 创建定时器。
        timer.schedule(new Runnable() {
            @Override
            public void run() { // 任务的执行逻辑。
                System.out.println("定时任务 3000"); // 打印出 hello world。
            }
        }, 3000);
        timer.schedule(new Runnable() {
            @Override
            public void run() { // 任务的执行逻辑。
                System.out.println("定时任务 2000"); // 打印出 hello world。
            }
        }, 2000);        timer.schedule(new Runnable() {
            @Override
            public void run() { // 任务的执行逻辑。
                System.out.println("定时任务 1000"); // 打印出 hello world。
            }
        }, 1000);
    
    }
  
}
相关推荐
程序员曼布8 分钟前
ThreadLocal源码深度剖析:内存管理与哈希机制
java·开发语言·哈希算法
hac132214 分钟前
IDEA快速上手Maven项目:模板选择 + 多模块拆分
java·maven·intellij-idea
边疆.15 分钟前
【C++】模板进阶
开发语言·c++·模板
o0向阳而生0o24 分钟前
36、C#中的⽅法声明参数关键字params,ref,out的意义及⽤法
开发语言·c#·.net
钢铁男儿28 分钟前
C# 方法(局部函数和参数)
java·数据库·c#
遇见你的雩风35 分钟前
Java---Object和内部类
java·开发语言
我们的五年37 分钟前
【QT】QT安装
开发语言·qt
逊嘘1 小时前
【纪念我的365天】我的创作纪念日
java
vibag1 小时前
启发式算法-禁忌搜索算法
java·算法·启发式算法·禁忌搜索
Echo``1 小时前
19:常见的Halcon数据格式
java·linux·图像处理·人工智能·windows·机器学习·视觉检测