线程池

[TOC]

线程池

1. 并发队列:阻塞队列和非阻塞队列

区别如下:

入队:
  • 非阻塞队列:当队列中满了的时候,放入数据,数据丢失
  • 阻塞队列:当队列满了的时候,进行等待,什么时候队列中有出队的数据,那么第 11 个再放进入
出队:
  • 非阻塞队列:如果现在队列中没有元素,取数据,得到的是 null
  • 阻塞队列:等待,什么时候放进去,再取出来

线程池是使用阻塞队列


2. 线程池原理:ThreadPoolExecutor 底层原理解析

2. 1线程的生命周期

从出生到死亡的阶段。

2.2 线程池工作原理
ThreadPoolExecutor t = new ThreadPoolExecutor(1, 3, 0, TimeUnit.MICROSECONDS, new LinkedBlockingDeque<>(3));

参数含义:

  • 1:核心线程数
  • 3:最大线程数
  • 0 和 TimeUnit.MICROSECONDS:当任务量大于队列长度需要创建线程的时候,新创建的这个线程假如把队列的任务都执行完了,那么等待新任务的空闲时间就是这个
  • new LinkedBlockingDeque<>(3)):阻塞队列:
    • 方法有参数:队列长度为 3
    • 方法无参数:队列长度为 Integer.MAX_VALUE

执行流程图如下所示:

2.3 举个例子
package com.test.jdk8;

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

public class TestThreadPoolExecutor {

    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 2, 3, TimeUnit.SECONDS, new LinkedBlockingDeque<>(3));
        // 利用线程池里的线程,开始执行任务
        // 执行第一个任务
        pool.execute(new TestThread());
        // 执行下面三个任务,放入阻塞队列,阻塞队列满:
        pool.execute(new TestThread());
        pool.execute(new TestThread());
        pool.execute(new TestThread());

        // 执行第 4个任务,创建新线程,分摊任务
        pool.execute(new TestThread());

        // 执行第 5 个任务,创建新创建,线程数大于最大线程数,报错
        pool.execute(new TestThread());


        // 关闭线程池
        pool.shutdown();
    }

}

class TestThread implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

3. 线程池的分类

1. newCachedThreadPool 可缓存线程池

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。示例代码如下:

package ThreadTest;

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

public class Test {

    public static void main(String[] args) {
        ExecutorService es = Executors.newCachedThreadPool();

        for (int i = 0; i < 100; i++) {
            // lambda 表达式, 实现 new Runnable() 的 run() 方法
            es.execute(() -> System.out.println(Thread.currentThread().getName()));
        }

        es.shutdown();

    }

}

查看 newCachedThreadPool 源码,可知:

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

该线程池的核心线程数为 0, 最大线程数为 Integer.MAX_VALUE,将会根据需要创建新线程,新线程的数量不大于 Integer.MAX_VALUE,线程执行任务结束后被释放可重复使用该线程执行另一个任务。

部分执行结果如下:

pool-1-thread-6
pool-1-thread-11
pool-1-thread-6
pool-1-thread-10
pool-1-thread-4
pool-1-thread-11
pool-1-thread-2
2. newFixedThreadPool 定长线程池

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。示例代码如下:

package ThreadTest;

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

public class Test {

    public static void main(String[] args) {
        ExecutorService es = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 100; i++) {
            // lambda 表达式, 实现 new Runnable() 的 run() 方法
            es.execute(() -> System.out.println(Thread.currentThread().getName()));
        }

        es.shutdown();

    }

}

查看 newFixedThreadPool 源码,可知:

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

传入的参数即为核心线程数和最大线程数,核心线程数和最大线程数的数量是相等的。

所以上述示例代码的执行结果的线程数就为 3,部分执行结果如下:

pool-1-thread-1
pool-1-thread-3
pool-1-thread-2
pool-1-thread-3
pool-1-thread-1
3. newScheduledThreadPool 定时线程池

创建一个定时线程池,支持定时及周期性任务执行。延迟执行示例代码如下:

package ThreadTest;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Test {

    public static void main(String[] args) {
        ScheduledExecutorService ses = Executors.newScheduledThreadPool(2);

        for (int i = 0; i < 100; i++) {
            ses.schedule(() -> System.out.println(Thread.currentThread().getName()), 3, TimeUnit.SECONDS);
        }

        ses.shutdown();

    }
}

查看 newScheduledThreadPool 源码,如下:

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

可知,newScheduledThreadPool 传入的参数即为核心线程数,最大线程数为 Integer.MAX_VALUE

上述代码通过设置延迟参数 3, 单位 TimeUnit.SECONDS,延迟三秒后执行任务,创建的线程数为 2。执行部分结果如下:

pool-1-thread-1
pool-1-thread-2
pool-1-thread-1
pool-1-thread-2
pool-1-thread-1
4. newSingleThreadExecutor 单例线程池

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。示例代码如下:

package ThreadTest;

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

public class Test {

    public static void main(String[] args) {
        ExecutorService es = Executors.newSingleThreadExecutor();

        for (int i = 0; i < 100; i++) {
            es.execute(() -> System.out.println(Thread.currentThread().getName()));
        }

        es.shutdown();

    }
}

查看 newSingleThreadExecutor 源码,可知:

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

核心线程数和最大线程数都为 1,执行完任务后线程的存活时间为 0,即创建出来的线程只会执行一次任务,保证单例执行。

运行部分结果如下:

pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 线程的两种创建方式:继承Thread类或者实现Runnable接口,Thread类本质上是实现了Runnable接...
    繁星追逐阅读 714评论 0 1
  • 线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任...
    安仔夏天勤奋阅读 1,133评论 0 10
  • 前言 掌握线程池是后端程序员的基本要求,相信大家求职面试过程中,几乎都会被问到有关于线程池的问题。我在网上搜集了几...
    Jay_Wei阅读 1,068评论 0 0
  • 前言 掌握线程池是后端程序员的基本要求,相信大家求职面试过程中,几乎都会被问到有关于线程池的问题。我在网上搜集了几...
    勤奋的码农阅读 1,391评论 0 1
  • 姥姥没在家,昨晚回来简单的打扫了一下房间,我们就睡觉了。 你和爸爸一觉睡到九点钟,我们一起去喝豆腐汤。你好久没喝了...
    素面迎风阅读 157评论 0 0

友情链接更多精彩内容