【JavaEE】线程池

目录

线程池概念

线程池优点

使用线程池

Executor接口:

ThreadPoolExecutor类:

构造方法

Executors工厂类:

工厂方法

线程池中的常用方法

线程池的工作流程

线程池的状态

RUNNING

SHUTDOWN

STOP

TIDYING

TERMINATED

简单实现线程池


线程池概念

线程池:把一个或多个线程通过统一的方式进行调度和重复使用的技术。相当于往一个池里放入一些线程,要使用了,就拿出来,用完了就还回去。


线程池优点

线程池相较于之前创建/销毁线程(操作系统实现),变成了取出来使用,不用了就还回去(用户代码实现),这样的方法更为高效,降低了资源消耗

由于在操作系统中许多情况是位置的,而有了线程池就会让我们掌握主动权,从而就提高了线程的可管理性和任务执行的响应速度


使用线程池

有关并发编程的使用,大多都在java.util.concurrent这个包下面,简称 juc 。

Executor接口:

所有有关线程池使用的类都是实现了该接口。


ThreadPoolExecutor类

使用这个类,就可以使用线程池了。

构造方法

这个类的构造方法有四个,其参数较为复杂。

对于这四个构造方法,可以看到第四个构造方法的参数最多,下面详细介绍一下参数的意义。

参数意义
int corePoolSize

核心线程数量

核心线程是线程池当中一直保存的线程。

int maximumPoolSize

最大线程数量

线程池中的线程 = 核心线程 + 临时线程

该参数就是二者之和

如果当前任务比较多,核心线程不够用,那么就可以多创建一些临时线程干活;如果当前任务少,并且持续了一段时间了,此时就可以销毁一些临时线程。

这个最大数量没有确定值。但是根据具体场景大致可以分为两类:

①CPU密集型

此时每个线程执行的任务都是很吃CPU的,此时就算搞再多的线程有没有用。设置成CPU内核数即可。

②IO密集型

此时每个线程执行的任务都是在等待IO(读写硬盘、读写网卡等),此时线程处于阻塞状态,不参与CPU的调度,这样就可以尽可能的多搞一些线程。

上面两种情况都是极端情况,实际上我们可以根据这两种情况作为参考,试一些数来选出最优情况。

long keepAliveTime

临时线程最大空闲时间

当临时线程空闲时间超过这个值时,此时就会被销毁。

TimeUnit unit

上述空闲时间的单位

枚举类型

NANOSECONDS : 纳秒

MICROSECONDS :微秒

MILLISECONDS : 毫秒

SECONDS : 秒

MINUTES : 分

HOURS : 小时

DAYS : 天

BlockingQueue<Runnable> workQueue

线程池的任务队列

此处使用阻塞队列,如果有任务就take()成功,没有就阻塞,不用一直take()

ThreadFactory threadFactory

创建线程

这是一个接口,new时需要实现

线程池中创建的线程由该它重写方法

Thread newThread(Runnable r);

线程池中的线程都是前台线程。

RejectedExecutionHandler handler

拒绝策略

拒绝策略是当线程池的任务队列满了之后,如果再往里面添加任务会有什么样的行为。标准库提供了四种拒绝策略。下图所示

①直接抛出异常

②多出来的任务,谁加的,谁负责执行

③丢弃最早的任务,给新任务腾地方

④丢弃最新的任务,保持不变

 代码演示:

import java.util.concurrent.*;

public class ThreadDemo29 {

    public static void main(String[] args) {

        ThreadPoolExecutor threadPoolExecutor =
                new ThreadPoolExecutor(8, 16, 20L,
                        TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>(8),
                        new ThreadFactory() {
                            @Override
                            public Thread newThread(Runnable r) {
                                Thread thread = new Thread(r);
                                return  thread;
                            }
                        },
        new ThreadPoolExecutor.CallerRunsPolicy());

        for (int i = 0; i < 10; i++) {
            threadPoolExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在执行该任务!");
                }
            });
        }

    }

}

可以看出,使用该类实现线程池是较为繁琐的。通常我们不使用该类实现线程池,而是使用一个工厂类。


Executors工厂类:

工厂类:简单的理解为用普通方法代替构造方法。

Executors里的工厂方法把上面的ThreadPoolExecutor类的构造方法加工了一下,使其变得更为简单好用给。

工厂方法

下面四种常用的方法都是静态方法,返回类型为 ExecutorService 

方法说明
Executors.newFixedThreadPool(int nThreads)创建含有 nThreads个 线程的线程池
Executors.newCachedThreadPool()创建的线程池中的线程数量不定,任务与线程数量成正相关
Executors.newScheduledThreadPool(int corePoolSize)

创建含有 corePoolSize个 核心线程的线程池

类似于之前的定时器,让任务延时执行。扫描线程的任务由线程池自己完成。

Executors.newSingleThreadExecuto()创建只有一个线程的线程池

代码演示

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

public class ThreadDemo27 {

    public static void main(String[] args) {

        ExecutorService pool1 = Executors.newFixedThreadPool(8);
        ExecutorService pool2 = Executors.newCachedThreadPool();
        ExecutorService pool3 = Executors.newScheduledThreadPool(2);
        ExecutorService pool4 = Executors.newSingleThreadExecutor();


        for (int i = 0; i < 5; i++) {
            pool1.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在执行任务!");
                }
            });
        }

        for (int i = 0; i < 5; i++) {
            pool2.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在执行任务!");
                }
            });
        }

        for (int i = 0; i < 5; i++) {
            pool3.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在执行任务!");
                }
            });
        }

        for (int i = 0; i < 5; i++) {
            pool4.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在执行任务!");
                }
            });
        }

    }

}

 


线程池中的常用方法

方法说明
Future<?>    submit(Runnable task)

执行提交的任务

返回值  是Future接口

当任务执行期间,我可以干其他事情,当任务结束后,返回值给Future,我想什么时候回来取结果都可以

参数类型  Runnable类型的任务

void    execute(Runnable command)

执行提交的任务

返回值  为空  与上面的submit的区别

参数类型  Runnable类型的任务

void    shutdown()

启动有序关闭 

先提交的任务先被执行,同时不接受任何新任务  不会报错,执行完成后正常退出

List<Runnable>    shutdownNow()

尝试立刻停止任务

如果有还在执行的任务,会抛出 java.lang.InterruptedException: sleep interrupted 异常

如果没有,则返回等待执行的任务列表

boolean    isShutdown()

判断线程池是否关闭

true  ---  关闭

false ---  未关闭

boolean    isTerminated()

判断线程池是不是完成所有任务而关闭

true  ---  完成所有任务关闭

false  ---  为完成所有任务关闭


线程池的工作流程

 

线程池的状态

线程池有五种状态。

RUNNING

线程池一旦被创建,就处于 RUNNING 状态,任务数为 0,能够接收新任务,对已排队的任务进行处理。

SHUTDOWN

不接收新任务,但能处理已排队的任务。调用线程池的 shutdown() 方法,线程池由 RUNNING 转变为 SHUTDOWN 状态。

STOP

不接收新任务,不处理已排队的任务,并且会中断正在处理的任务。调用线程池的 shutdownNow() 方法,线程池由(RUNNING 或 SHUTDOWN ) 转变为 STOP 状态。

TIDYING

①SHUTDOWN 状态下,任务数为 0, 其他所有任务已终止,线程池会变为 TIDYING 状态,会执行 terminated() 方法。线程池中的 terminated() 方法是空实现,可以重写该方法进行相应的处理。

②线程池在 SHUTDOWN 状态,任务队列为空且执行中任务为空,线程池就会由 SHUTDOWN 转变为 TIDYING 状态。

③线程池在 STOP 状态,线程池中执行中任务为空时,就会由 STOP 转变为 TIDYING 状态。

TERMINATED

线程池彻底终止。线程池在 TIDYING 状态执行完 terminated() 方法就会由 TIDYING 转变为 TERMINATED 状态。


简单实现线程池

代码注释中详解。

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

class MyThreadPool {
    // 使用阻塞队列保存任务
    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();

    // 构造方法中先创建几个线程
    public MyThreadPool (int n) {
        //
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(() -> {
                while (true) {
                    try {
                        // 一直从阻塞队列中拿任务
                        // 如果没有任务就自动阻塞
                        Runnable runnable = queue.take();
                        runnable.run();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            t.start();
        }
    }

    // 提交任务给线程池中的阻塞队列
    public void submit(Runnable task) {
        try {
            queue.put(task);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ThreadDemo28 {

    public static void main(String[] args) {

        MyThreadPool myThreadPool = new MyThreadPool(10);
        for (int i = 0; i < 10; i++) {
            myThreadPool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在执行任务!");
                }
            });
        }

    }

}


有什么错误评论区指出。希望可以帮到你。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值