Java线程池学习

众所周知,Java不仅提供了线程,也提供了线程池库给我们使用,那么今天来学学线程池的具体使用以及线程池基本实现原理分析。


ThreadPoolExecutor

ThreadPoolExecutor的构造方法:

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}

参数解释如下:

  • corePoolSize : 核心池大小,即线程的数量
  • maximumPoolSize : 线程池最大数量
  • keepAliveTime : 线程池中的线程的保活时间,超时后线程会退出直到线程池中的线程数量等于corePoolSize,如果allowCoreThreadTimeout为true,线程数量会降为0
  • workQueue:线程池采用的缓冲队列,常用的有:java.util.concurrent.ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue
  • handler:当线程池中线程数量达到maximumPoolSize时,仍有任务需要创建线程来完成,则handler采取相应的策略,默认的ThreadPoolExecutor.AbortPolicy(),即会抛弃该任务。

线程池中可能的几种状态:

  • 当线程数量小于corePoolSize时,任务来时会创建新的线程来处理,并把该线程加入线程队列中(实际上是一个hashset)(此步骤需要获取全局锁,ReentryLock)

  • 如果当前线程数量达到了corePoolSize,任务来时将任务加入BlockingQueue

  • 如果任务列队满了无法加入新的任务时,会创建新的线程(同样需要获取全局锁)

  • 如果线程池数量达到maximumPoolSize,并且任务队列已满,新的任务将被拒绝

注意:获取全局锁是一个非常影响性能的因素,所以线程池会尽量执行第二步,因为此步骤不需要获取全局锁。

各种任务队列(BlockingQueue)的区别

  • ArrayBlockingQueue: 基于数组的有界阻塞队列,FIFO

  • LinkedBlockingQueue:基于链表的无界阻塞队列,FIFO,吞吐量高于ArrayBlockingQueue,Executors.newFixedThreadPool()使用了该队列

  • SynchronousQueue:一个不存储元素的队列,一进一出吞吐量高于LinkedBlockingQueue,Executors.newCachedThreadPool()使用了该队列

  • PriorityBlockingQueue:阻塞优先队列

RejectedExecutionHandler饱和拒绝策略

  • AbortPolicy:直接抛出异常
  • CallerRunsPolicy:只用调用者所在线程来运行任务
  • DiscardOldestPolicy:丢弃队列里最近的任务,并执行当前任务
  • DiscardPolicy:不处理,不丢弃

提交一个任务

两种方式:

threadPool.submit(new Runnable() {
@Override
public void run() {
//
}
});

上面是第一种,通过submit方法提交,该方法有返回值,返回类型是Future<?>类型,

threadPool.execute(new Runnable() {
@Override
public void run() {
//
}
});

上面是第二种,通过execute执行,无返回值。

关闭线程池

shutdown()和showdownNow()两种方法,都是调用了interruptIdleWorkers()方法去遍历线程池中的工作线程,然后去打断它们,代码如下:

private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock()) {
try {
//中断线程
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}

不同的是shutdown是把线程的状态置为SHUTDOWN而shutdownNow是STOP。

调优

  • 尽量使用有界队列
  • 线程池中的线程数量应该根据场景来合理配置,例如CPU密集型的任务就应该配置尽量少的线程数量

Executors工具类

主要介绍工具类Executors创建的三种线程池:

ExecutorService executorService = Executors.newSingleThreadExecutor();
ExecutorService executorService1 = Executors.newFixedThreadPool(10);
ExecutorService executorService2 = Executors.newCachedThreadPool();

FixedThreadPool

源码如下:

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

可以看出core和max数量是一样的,因为使用的LinkedBlockingQueue是无界阻塞队列,因此达到coreSize后不会继续创建线程而是阻塞在队列那了。

SingleThreadExecutor

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

只创建一个线程的线程池。

CachedThreadPool

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

使用了SynchronousQueue这个没有容量的阻塞队列,线程池的coreSize为0,maxSize为无限大,所以当生产者提供任务的速度大于消费者处理任务的时候,可能会无线创建线程,从而导致系统资源(CPU,内存等)耗竭。

【Java多线程】线程池学习的更多相关文章

  1. [Java多线程]-线程池的基本使用和部分源码解析(创建,执行原理)

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 多线 ...

  2. 跟我学Java多线程——线程池与堵塞队列

    前言 上一篇文章中我们将ThreadPoolExecutor进行了深入的学习和介绍,实际上我们在项目中应用的时候非常少有直接应用ThreadPoolExecutor来创建线程池的.在jdk的api中有 ...

  3. java多线程--线程池的使用

    程序启动一个新线程的成本是很高的,因为涉及到要和操作系统进行交互,而使用线程池可以很好的提高性能,尤其是程序中当需要创建大量生存期很短的线程时,应该优先考虑使用线程池. 线程池的每一个线程执行完毕后, ...

  4. java多线程-线程池

    线程池(Thread Pool)对于限制应用程序中同一时刻运行的线程数很有用.因为每启动一个新线程都会有相应的性能开销,每个线程都需要给栈分配一些内存等等. 我们可以把并发执行的任务传递给一个线程池, ...

  5. Java多线程——线程池

    系统启动一个新线程的成本是比较高的,因为它涉及到与操作系统的交互.在这种情况下,使用线程池可以很好的提供性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池. 与数据库连接池类似 ...

  6. java多线程——线程池源码分析(一)

    本文首发于cdream的个人博客,点击获得更好的阅读体验! 欢迎转载,转载请注明出处. 通常应用多线程技术时,我们并不会直接创建一个线程,因为系统启动一个新线程的成本是比较高的,涉及与操作系统的交互, ...

  7. java多线程----线程池源码分析

    http://www.cnblogs.com/skywang12345/p/3509954.html 线程池示例 在分析线程池之前,先看一个简单的线程池示例. 1 import java.util.c ...

  8. 深入理解Java多线程——线程池

    目录 为什么需要线程池 定义 ThreadPoolExecutor 工作队列workQueue 不同的线程池 Executor 线程池的工作原理 线程池生命周期 线程池增长策略 线程池大小的设置 线程 ...

  9. Java多线程-线程池ThreadPoolExecutor构造方法和规则

    为什么用线程池 原文地址 http://blog.csdn.net/qq_25806863/article/details/71126867 有时候,系统需要处理非常多的执行时间很短的请求,如果每一个 ...

  10. Java多线程-----线程池详解

    1. 线程池的实现原理 提交一个任务到线程池中,线程池的处理流程如下: 判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务.如果 ...

随机推荐

  1. Cycle Sort

    Cycle sort的思想与计数排序太像了,理解了基数排序再看这个会有很大的帮助, 圈排序与计数排序的区别在于圈排序只给那些需要计数的数字计数,先看完文章吧,看完再回来理解这一句话 所谓的圈的定义,我 ...

  2. aio 爬虫,去重,入库

    #aio 爬虫,去重,入库 import asyncio import aiohttp import aiomysql import re from pyquery import PyQuery st ...

  3. pyttsx3 winsound win32api.MessageBox使用案例

    import requests,time from lxml import etree import win32api,win32con import winsound import pyttsx3 ...

  4. java 7修改文件权限

    Full control over file attributes is available in Java 7, as part of the "new" New IO faci ...

  5. Codeforces Round #411 (Div. 2) A-F

    比赛时候切了A-E,fst了A Standings第一页只有三个人挂了A题,而我就是其中之一,真™开心啊蛤蛤蛤 A. Fake NP time limit per test 1 second memo ...

  6. springmvc4处理get和post请求中文乱码问题

    1.在springmvc4处理get和post请求的问题 参看大牛博客连接:https://blog.csdn.net/qq_41665356/article/details/80234392

  7. 限制printk打印频率函数printk_ratelimit【转】

    转自:http://blog.csdn.net/lkkey80/article/details/45190095 版权声明:博文地址 http://blog.csdn.net/lkkey80?view ...

  8. 【UOJ#9】vfk的数据

    我感觉这题可以出给新高一玩2333 #include<bits/stdc++.h> #define N 10005 using namespace std; string s[N]; in ...

  9. xcode 配置系统环境变量 Preporocessing 预编译宏的另一种写法, 系统的DEBUG 由来

    在某些项目中看到一些环境变量类似宏的东西 比如叫ENVIRONMENT, 但发现还找不到具体这个宏是什么值, 那是因为他实在Preprocessing里配置了这个宏的值, 他能配置debug/rele ...

  10. freemark学习

    学习地址: http://blog.csdn.net/hejinxu/article/details/6694890   对freemarker的用法与语法进行了详细的讲解 http://freema ...