Java多线程(三)——线程池及定时器

2023-05-16

线程池就是一个可以复用线程的技术。前面三种多线程方法就是在用户发起一个线程请求就创建一个新线程来处理,下次新任务来了又要创建新线程,而创建新线程的开销是很大的,这样会严重影响系统的性能。线程池就相当于预先创建好几个线程(招聘几个打工人),来分配之后要处理的任务(干活)

线程池的接口:ExecutorService

线程池对象

  1. 使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象

可以看到有7个参数,通过这些参数设置线程池的规模和特征。ExecutorService的常用方法有execute、submit、shutdown、shutdownNow。

  1. ThreadPoolExecutor构造器的参数:

  1. 1:指定线程池的线程数量(核心线程): corePoolSize

  1. 2:指定线程池可支持的最大线程数: maximumPoolSize

  1. 3:指定临时线程的最大存活时间: keepAliveTime

  1. 4:指定存活时间的单位(秒、分、时、天): unit

  1. 5:指定任务队列: workQueue

  1. 6:指定用哪个线程工厂创建线程: threadFactory

  1. 7:指定线程忙,任务满的时候,新任务来了怎么办: handler,默认丢弃任务并抛出RejectedExecutionException异常。

  1. ThreadPoolExecutor创建线程池对象

ExecutorService pool=new ThreadPoolExecutor(3,6,8, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(6), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
  1. 线程池处理Runnable任务 ——pool.execute()

首先实现Runnable接口,重写run方法。然后创建MyRunnable任务对象,只不过这里不是把MyRunnable任务对象交给Thread处理,而是使用线程池pool的execute()方法。

class myRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " ——>" + i);
        }
        try {
            System.out.println(Thread.currentThread().getName() + " —— 休眠");
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + " —— 启动");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
//
        Runnable target=new myRunnable();
        pool.execute(target);
  1. 线程池处理Callable任务 ——pool.submit()

定义类实现Callable接口,重写call方法,封装要做的事情。然后把Callable对象进行submit(),并且可以返回执行后的结果。

class myCallable implements Callable<String> {
    private int n;
    public myCallable(int n) {
        this.n = n;
    }
    @Override
    public String call() throws Exception {
        int s=0;
        for (int i = 0; i < n; i++) {
            s+=n;
        }
        return Thread.currentThread().getName()+" 1+...+" + n +
                ", 子线程执行结果: "+s;
    }
}
//
        Callable myCallable=new myCallable(10);
        Future<String> f1 = pool.submit(myCallable);
        System.out.println(f1.get());
  1. 使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象

Executors的底层其实也是基于线程池的实现类ThreadPoolExecutor创建线程池对象的。Executors得到线程池对象的常用方法:

方法

说明

弊端

public static ExecutorService newCachedThreadPool()

线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了一段时间则会被回收掉。

允许创建的线程数量最大上限是Integer.MAX_VALUE,非常非常大

可能会创建大量线程,出现OOM错误( 内存溢出 java.lang.OutOfMemoryError )

public static ExecutorService newFixedThreadPool(int nThreads)

创建固定线程数量的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程替代它。

允许请求的任务队列长度是Integer.MAX_VALUE,可能会堆积大量请求,出现OOM错误

public static ExecutorService newSingleThreadExecutor ()

创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程。

允许请求的任务队列长度是Integer.MAX_VALUE,可能会堆积大量请求,出现OOM错误

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务。

允许创建的线程数量最大上限是Integer.MAX_VALUE,

可能会创建大量线程,出现OOM错误

Executors不适合做大型互联网场景的线程池方案,建议使用ThreadPoolExecutor来指定线程池参数,这样可以明确线程池的运行规则,规避资源耗尽的风险。

  1. 定时器

定时器是一种控制任务延时调用,或者周期调用的技术。定时器的实现方式有两种:

方式一:Timer。创建Timer定时器对象,然后开启定时器。这是一种单线程方法,处理多个任务按照顺序执行,存在延时与设置定时器的时间有出入。可能因为其中的某个任务的异常使Timer线程死掉,从而影响后续任务执行。

    Timer t=new Timer();
    t.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定时器~");
            }
        },3000,3000);

方式二: ScheduledExecutorService定时器。基于线程池,某个任务的执行情况不会影响其他定时任务的执行。先得到线程池对象,然后再进行周期调度方法。

        ScheduledExecutorService pool=Executors.newScheduledThreadPool(3);
        pool.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定时器");
            }
        },3,3,TimeUnit.SECONDS);
  1. 并发与并行

CPU同时可以处理线程的数量有限,所以CPU会轮询为每个线程服务,只是由于CPU切换的速度很快,给我们的感觉这些线程在同时执行,其实可能只是并发。所以说多个线程其实是并发与并行同时进行的

  1. 线程的6种状态

NEW(新建)

线程刚被创建,但是并未启动。

Runnable(可运行)

线程已经调用了start()等待CPU调度。

Blocked(锁阻塞)

线程在执行的时候未竞争到锁对象,则该线程进入Blocked状态。

Waiting(无限等待)

一个线程进入Waiting状态,另一个线程调用notify或者notifyAll方法才能够唤醒。

Timed Waiting(计时等待)

同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。带有超时参数的常用方法有Thread.sleep 、Object.wait。

Teminated(被终止)

因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java多线程(三)——线程池及定时器 的相关文章

随机推荐

  • CAP理论/AP架构/CP架构

    要想分析CP和baiAP两种模式的优缺点 xff0c 首先要du弄zhi懂什么是CAP C Consistency 强一致性 daoA Available 可用性 P Partition tolerance 分区容错性 在分布式中zhuan
  • 分布式开发(6)-基于redis分布式session

    启动类中添加 64 EnableRedisHttpSession配置类中添加 spring session store type redis 配置session失效时间 server servlet session timeout 30m
  • Error: Can't open display: :0.0

    linux下非root用户启动图形化界面报错 oratest 64 dudu xclock Xlib connection to 34 0 0 34 refused by server Xlib No protocol specified
  • linux安装docker

    https www funtl com zh docs docker Ubuntu E5 AE 89 E8 A3 85 Docker html E5 87 86 E5 A4 87 E5 B7 A5 E4 BD 9C
  • 一图看懂git fetch和git pull关系

    可以看出当我们想在自己的workspace切换分支的时候 xff0c 其实切换的是本地的repository的分支 xff0c 而如果远程新建了一个分支 xff0c 直接checkout是check不了的 xff0c 所以需要先fetch将
  • 分布式开发(7)-基于redisson分布式锁解决定时任务重复执行

    定时任务在部署多台服务时会出现重复执行的问题 xff0c 所以要借助基于reidis的redisson分布式锁来保证每次只执行一次 配置 2 1 引入redisson lt dependency gt lt groupId gt org s
  • k8s学习

    集群组件 核心概念
  • 一张图看懂inner join、left join、right join的区别

  • idea创建react项目

    最近没事做想学习一点前端的知识 xff0c 由于平时都使用idea作为后端开发 xff0c 这里也使用idea来开发react 安装nodejs 直接官网下载安装 xff0c https nodejs org en download ide
  • react学习

    1 安装yarn NPM xff08 Node Package Manager xff09 作为Node js的依赖包管理器便应运而生 xff0c 而YARN则是针对NPM的一些不太优秀的地方设计开发的 npm install g yarn
  • 鸿蒙开发学习

    1 app三大组成部分 组件 屏幕展示出来的元素 xff0c 如按钮 xff0c 图片 xff0c 下载进度条布局 多个组件的摆放方式叫做布局 xff0c 组件必须添加到布局中才能够显示出来事件 可以被组件识别的操作 xff0c 如单击 双
  • dubbo远程调用HessianProtocolException: expected map/object at java.lang.Long报错解决

    调用的接口使用到了 96 List of 96 方法 xff0c 这个方法的源码是使用的 96 ImmutableCollections 96 xff0c 这个集合不支持序列化 xff0c 所以报错 xff0c 改成 96 Collecti
  • ubuntu安装minikube

    创建root账号并关闭防火墙开启ssh sudo passwd sudo ufw status sudo ufw disable sudo apt install openssh server root用户下修改源 cp etc apt s
  • kde桌面安装使用vnc

    可能大家比较习惯的都是使用ssh作为连接一个linux主机的工具 反正在这之前我是如此 不过面对新需求 xff0c 有点力不从心 xff1a linux主机上面运行了一台虚拟机 反正说白了就是瞎折腾 主机环境是archlinux 通过arc
  • centos7.5安装k8s集群

    vmware安装centos7 5 下载镜像http mirrors aliyun com centos 7 isos x86 64 xff0c 下载dvd版本即可 xff0c 有个桌面用起来比较方便
  • Ubuntu20安装k8s单机版

    1 安装kubectl xff0c 用来操作k8s curl LO 34 https dl k8s io release curl L s https dl k8s io release stable txt bin linux amd64
  • k8s学习

    1 k8s简单使用 创建一个deploy kubectl create deployment nginx image 61 nginx 暴漏pod xff0c 也就是创建一个类型为NodePort的service kubectl expos
  • 关于win11docker启动总是报一堆乱码错误的解决办法

    每次启动都会报System InvalidOperationException Failed to set version to docker desktop e后面跟一堆乱码错误 方法一 xff1a netsh winsock reset
  • java jsonarray 删除,如何使用Java删除JSONArray元素

    My JsonArray is 34 Id 34 null 34 Name 34 34 One New task 34 34 StartDate 34 34 2010 02 03T05 30 00 34 34 EndDate 34 34 2
  • Java多线程(三)——线程池及定时器

    线程池 就是一个可以复用线程的技术 前面三种多线程方法就是在用户发起一个线程请求就创建一个新线程来处理 xff0c 下次新任务来了又要创建新线程 xff0c 而创建新线程的开销是很大的 xff0c 这样会严重影响系统的性能 线程池就相当于预