详解线程以及ThreadPoolExecutor 的参数含义及源码执行流程

线程(Thread)

是并发编程的基础,也是程序执行的最小单元,他依托进程而存在
一个进程中可以包含多个线程,多线程可以共享一块内存空间和一组系统资源
因此线程之间的切换更加节省资源、更加轻量化,也因此被称为轻量级的进程

线程的状态有哪些, 他是如何工作的?

线程的状态在 JDK 1.5 之后以枚举的方式被定义在 Thread 的源码中,它总共包含以下 6 个状态:

  • NEW,新建状态,线程被创建出来,但尚未启动时的线程状态;
  • RUNNABLE,就绪状态,表示可以运行的线程状态,它可能正在运行,或者是在排队等待操作系统给它分配 CPU 资源;
  • BLOCKED,阻塞等待锁的线程状态,表示处于阻塞状态的线程正在等待监视器锁,比如等待执行 synchronized 代码块或者使用 synchronized 标记的方法;
  • WAITING,等待状态,一个处于等待状态的线程正在等待另一个线程执行某个特定的动作,比如,一个线程调用了Object.wait()方法,那它就在等待另一个线程调用Object.notify()Object.notifyAll()方法;
  • TIMED_WAITING,计时等待状态,和等待状态(WAITING)类似,它只是多了超时时间,比如调用了有超时时间设置的方法Object.wait(longtimeout)Thread.join(long timeout)等这些方法时,它才会进入此状态;
  • TERMINATED,终止状态,表示线程已经执行完

start() 和 run() 的区别

  • 先从 Thread 源码来看,start() 方法属于 Thread 自身的方法,并且使用了 synchronized 来保证线程安全。
  • run() 方法为 Runnable 的抽象方法,必须由调用类重写此方法,重写的 run() 方法其实就是此线程要执行的业务方法,
  • 从执行的效果来说,start() 方法可以开启多线程,让线程从 NEW 状态转换成 RUNNABLE 状态,而 run() 方法只是一个普通的方法。
  • 其次,它们可调用的次数不同,start()方法不能被多次调用,否则会抛出java.lang.IllegalStateException;而run()方法可以进行多次调用,因为它只是一个普通的方法而已。

线程优先级

线程的优先级可以理解为线程抢占 CPU 时间片的概率,优先级越高的线程优先执行的概率就越大,但并不能保证优先级高的线程一定先执行。

在程序中我们可以通过 Thread.setPriority()来设置优先级

线程的常用方法

join()

在一个线程中调用 other.join() ,这时候当前线程会让出执行权给 other 线程,直到 other 线程执行完或者过了超时时间之后再继续执行当前线程。 从源码中可以看出 join() 方法底层还是通过 wait() 方法来实现的。

yield()

yield() 方法表示给线程调度器一个当前线程愿意出让 CPU 使用权的暗示,但是线程调度器可能会忽略这个暗示。

yield() 执行非常不稳定,线程调度器不一定会采纳 yield() 出让 CPU 使用权的建议。

线程池

为什么要用线程池

  1. 降低资源消耗。通过重复利用已创建的线程降低线程创建、销毁线程造成的消耗。
  2. 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控

在阿里巴巴的Java开发手册中规定

线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式。这样处理方式让写的读者更加明确线程池的运行规则,规避资源耗尽的风险。

说明:Excutors返回的线程池对象的弊端如下:

  • FixedThredPoolSingleThreadPool:允许的请求队列长度为 Integer.MAX_VALUE可能会堆积大量的请求,从而导致OOM
  • CachedThreadPoolScheduledThreadPool : 允许的创建线程数量为 Integer.MAX_VALUE可能会创建大量的线程, 从而导致OOM

查看Executors 源码会发现: Excutors.newFixedThreadPool()Excutors.newSingleThreaExecutor()Executors.newCachedThreadPool() 等方法的底层都是通过 ThreadPoolExecutor实现的

ThreadPoolExecutor核心参数

ThreadPoolExecutor的核心参数指的是他在创建时需要传递的参数:

ThreadPoolExecutor的构造方法:

    /**
     * @param corePoolSize  表示线程池的常驻核心线程数
     * @param maximumPoolSize 线程池中允许的最大线程数
     * @param keepAliveTime 表示线程的存活时间
     * @param unit 表示存活时间{keepAliveTime} 的单位
     * @param workQueue  表示线程执行的任务队列
     * @param threadFactory 表示线程的创建工厂(通常创建时我们使用默认的)
     * @param handler  表示指定线程池的拒绝策略
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            //maximumPoolSize 必须大于0, 且必须大于 corePoolSize
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

线程池执行流程

在这里插入图片描述

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
    	// 当前工作的线程数小于核心线程数
        if (workerCountOf(c) < corePoolSize) {
            //创建新的线程执行此任务
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
    	// 检查线程池是否处于运行状态, 如果是则把任务添加到队列
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            // 再次 检查线程池是否处于运行状态,防止在第一次校验通过后线程池关闭。
            // 如果是非运行状态, 则将刚加入队列的的任务移除。
            if (! isRunning(recheck) && remove(command))
                reject(command);
            // 如果线程池的线程数为 0 时,(当corePoolSize设置为 0 时 会发生)
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);  //新建线程执行任务
        }
    	// 核心线程都在忙且队列都已经爆满, 尝试新启动一个线程执行失败
        else if (!addWorker(command, false))
            // 执行拒绝策略
            reject(command);
    }
private boolean addWorker(Runnable firstTask, boolean core) {...}

其中AddWorker方法的参数说明:

  • firstTask
    线程应首先运行的任务,如果没有则可以设置为 null
  • core
    判断是否可以创建线程的阈值(最大值)
    如果等于true 则表示使用corePoolSize作为阈值
    fase 则表示使用 maximumPoolSize作为阈值

execute() VS submit()

都是用来执行线程池任务

主要区别:

submit() 方法可以接受线程池执行的返回值, 属于ExecutorService接口的方法。

execute() 不能接受返回值, 属于Executor接口的方法
在这里插入图片描述

线程池的拒绝策略

Java自动的拒绝策略
AbortPolicy终止策略,线程池会抛出异常并终止执行,他是默认的拒绝策略
CallerRunsPolicy把任务交给当前线程来执行
DiscardPolicy忽略此任务(最新的任务)
DIsCardOldestPolicy忽略最早的任务(最先加入队列的任务)

我们也可以通过重写 rejectedExecution()来自定义拒绝策略

还可以通过重写beforeExecution()afterExecute()来实现ThreadPoolExecutor的扩展功能

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值