Java线程池总结

线程是个吃资源大户,如果没有很好地管理线程,容易造成许多问题,所以线程池应运而生

  • 为什么要使用线程池

1. 降低系统资源损耗,线程的创建、运行、销毁都需要消耗系统资源,通过线程池可以达到线程的复用,避免无用的消耗

2. 提高响应速度,任务到达时可以直接使用线程,不需要再等待线程的创建

3. 提高线程的可管理性

  • 线程池执行逻辑

逻辑顺序:核心线程——>阻塞队列——>最大线程数——>拒绝策略

  • 怎么使用线程池

简单的方法就是通过使用Executors提供的方法来创建,例如:

newFixedThreadPool(),创建固定大小的线程池

newSingleThreadExecutor():创建只有一个线程的线程池

newCachedThreadPool():创建无上限的线程池

但是快捷的方法大部分时间并不能满足于实际业务需求,在平时使用时,普遍采用调用ThreadPoolExecutor的构造方法来进行线程池的创建,即

几个参数的定义分别是:

  1. corePoolSize:核心线程池的大小,里面的线程不会被回收。如果核心线程池没满,就会创建新的线程,满了的话就会存储到阻塞队列里

  2. maximumPoolSize:线程数的上限。当阻塞队列已满时,并且当前线程池线程个数没有超过maximumPoolSize的话,就会创建新的线程来执行任务

  3. keepAliveTime:空闲线程存活时间。如果线程线程池满了,就会将超过keepAliveTime的空闲的线程清除

  4. unit:keepAliveTime的时间单位

  5. workQueue:用来保存任务的阻塞队列

  6. threadFactory:线程创建工厂

  7. handler:拒绝策略,线程池无法容纳更多的线程来处理任务,那么这些任务就会被拒绝,拒绝的策略如下

  • AbortPolicy: 拒绝任务,并抛出RejectedExecutionException异常
  • CallerRunsPolicy:只用调用者所在的线程来执行任务
  • DiscardPolicy:不做处理,直接忽略
  • DiscardOldestPolicy:丢弃掉阻塞队列中存放时间最久的任务,执行当前任务

来实际测试一下

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ExecutorTest {

    public static void main(String[] args) {
        //不使用线程池
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new runnable1());
            thread.start();
        }
        //使用线程池后,可以进行线程复用
        ThreadPoolExecutor executorService = new ThreadPoolExecutor(
                2, 10, 5, TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(3),
                new ThreadPoolExecutor.CallerRunsPolicy());
        for (int i = 0; i < 10; i++) {
            executorService.execute(new runnable1());
        }
    }

    static class runnable1 implements Runnable {

        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "线程被调用了");
        }
    }
}

输出结果为

上面是使用execute()方法来提交任务,主要是针对不需要返回值的任务,所以无法知道任务是否执行成功

还可以使用submit()方法,这个方法会返回一个Future对象,通过这个对象来判断任务是否执行成功

 Future future = executorService.submit(new runnable1());

使用Future类的get方法可以获取返回值,get()方法会进行阻塞,get()方法会阻塞当前线程直到任务完成,而使用get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完

关闭线程池可以通过调用线程池的shutdown或shutdownNow方法,前者不会关闭暂停正在执行的线程,后者则是关闭所有线程,使用情况据实际场景而定

executorService.shutdown();
executorService.shutdownNow();
  • 总结

1. 避免使用Executor的快捷方法,防止因为无界队列出现OOM或因为线程数设置不当导致线程数耗尽的情况

2. submit方法也是调用了execute方法,不过多加了Future类的返回值

3. 参数阻塞队列需要设置界限,使用无界队列会造成任务一直积压大量使用内存导致系统崩溃

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

方木丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值