理解java中的线程池

1.引入线程池的原因

对于多线程编程,处理每个请求都要创建一个线程,这不仅要花费时间在创建线程的过程中,还会出现创建线程过多未释放导致的系统内存不足,内存溢出问题,因此引入线程池的概念。线程池,就是在一个容器中创建适量的线程,在程序访问的时候直接调用该线程即可访问。

2.类比数据库连接池

数据库连接池与线程池类似,dao层访问数据库时,首先会,加载驱动,建立连接,而每次频繁的建立连接肯定会大大降低系统运行效率,因此,数据库连接池出现了,下面以一张图进行说明:

如上图,没连接池时访问一次数据库便建立一个Connection,用完,连接释放,添加数据库连接池后,是连接先建立好或者使用时建立,用完,不释放,而是放在数据库连接池中,供下一次请求调用。

3.线程池的执行流程

在具体了解线程池之前,有必要先了解一下,线程池的执行流程。

<1>.当提交一个新任务到线程池中时,先判断线程池中的当前线程数是否大于线程池基本大小(corePoolSize),小于corePoolSize:创建一个新任务来执行当前线程。

<2>.大于corePoolSize,将任务存放入阻塞队列中(假定此时的阻塞队列是有界的),阻塞队列未满:存放入阻塞队列,等待线程执行。

<3>队列已满:判断线程池中的线程数量是否大于maximumPoolSize(线程池的最大容量),小于,创建新线程来处理该任务。

<4>线程池中的线程数量大于maximumPoolSize(线程池的最大容量),即线程池中的所有线程都处于工作状态,此时任务会被拒绝,会交给饱和策略来进行处理。

4.了解java.util.concurrent包下的ThreadPoolExecutor类

ThreadPoolExectuor类是线程池的核心类,其可以创建线程池。

<1> 构造方法(创建线程池时使用):

 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
            long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue);

    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
            long keepAliveTime, TimeUnit unit,
            BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory);

    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
            long keepAliveTime, TimeUnit unit,
            BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler);

    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
            long keepAliveTime, TimeUnit unit,
            BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
            RejectedExecutionHandler handler);

参数:

corePoolSize - 池中所保存的线程数,包括空闲线程,线程池中的基本线程数。

maximumPoolSize - 池中允许的最大线程数。

keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。即放在阻塞队列中的线程的最大等待时间,

unit - keepAliveTime 参数的时间单位。

workQueue - 执行前用于保持任务的阻塞队列。此队列仅保持由 execute 方法提交的 Runnable 任务。

threadFactory - 执行程序创建新线程时使用的工厂。

handler - 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序,拒绝执行任务时的策略。

<2>主要方法:

1》public void execute(Runnable command):向线程池提交一个任务 command,交由线程池去执行。若执行失败则会调用RejectedExecutionHandler,拒绝执行任务策略进行处理。

具体源码:

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
        if (runState == RUNNING && workQueue.offer(command)) {
            if (runState != RUNNING || poolSize == 0)
                ensureQueuedTaskHandled(command);
        }
        else if (!addIfUnderMaximumPoolSize(command))
            reject(command); // is shutdown or saturated
    }
}

execute方法就是按照上面的线程执行流程来的,

《2 submit 和execute()一样,用于向线程池提交任务,

区别:submit提交具有返回值的任务,execute()无返回值。

《3 shutdown,shutdownNow

区别:主要在是否关闭正在执行的线程。

shutdown:逐个遍历线程池中的线程,调用中断处理,中断没有正在执行的线程。

shudownNow:将线程池的状态设置为stop,停止所有正在执行或停止任务的线程。

5.几种类与接口间的继承,实现关系图

类ThreadPoolExecutor继承类AbstractExecutor

类AbstractExecutor实现接口ExecutorService接口

ExecutorService接口继承Executor接口

6.几种具ThreadPoolExecutor

<1>FixedThreadPool,可重用固定线程数的线程池

其创建过程调用 ThreadPoolExecutor的构造方法

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
            long keepAliveTime, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());

其中:

corePoolSize, maximumPoolSize为一个具体的固定的值。

keepAliveTime:0L,即多余空闲线程等待新任务的时间为0s,线程池中若存在空闲线程,则会立即被终止。

这儿阻塞队列,采用了LinkedBlockingQueue无界阻塞队列(队列的最大容量为Integer.MAX_VALUE)。

采用无界队列带来的影响:

1.线程池中的正在执行的线程数目不会超过corePoolSize

原因:超过corePoolSize,便会放在阻塞队列中等待被执行,阻塞队列永远不会满,不会再创建新的线程来执行任务。

2.maximumPoolSize,线程池中的最大线程数成为无用参数。很好理解。

3.FixedThreadPool,永远不会拒绝任务,不会使用上面说的几种拒绝策略。(永远不会达到线程池的最大线程数目)

<2>SingleThreadPool(单个线程池)

调用ThreadPoolExecutor的构造方法:

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());

其中:

corePoolSize, maximumPoolSize 设置为1,使用单个线程。

keepAliveTime:0L,即多余空闲线程等待新任务的时间为0s,线程池中若存在空闲线程,则会立即被终止。

这儿阻塞队列,采用了LinkedBlockingQueue无界阻塞队列,单个线程无限反复的从阻塞队列中拿出任务进行执行。

同样无界队列带来的问题,SingleThreadPool也有。

<3>.CachedThreadPool(按需分配的线程池)

调用ThreadPoolExecutor的构造方法:

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());

其中:

corePoolSize,默认设置为0,

maximumPoolSize ,设置Integer.MAX_VALUE,即线程池的最大线程数是无界的。

keepAliveTime:60L,即多余空闲线程等待新任务的时间为60s,

这儿阻塞队列,采用了SynchronousQueue无界阻塞队列,SynchronousQueue:一个没有容量的阻塞队列,在插入之前必须有线程从队列中拿走的操作,

CachedThreadPool:按需分配的线程池,就是主线程offer任务1,线程池中查找有无空闲线程,无则创建新线程让其执行任务1,同时,任务2来了,查看有空闲线程,让其执行任务2,还有空闲线程,等待60s若还没有任务执行,则终止。

具体实践:

1.线程池应用之自主实现一个线程池

2.使用Executors中提供的线程池实现生产者-消费者模型

参考自:java并发编程的艺术(方腾飞)

时间: 2024-10-09 20:32:13

理解java中的线程池的相关文章

Java中的线程池

综述 在我们的开发中经常会使用到多线程.例如在Android中,由于主线程的诸多限制,像网络请求等一些耗时的操作我们必须在子线程中运行.我们往往会通过new Thread来开启一个子线程,待子线程操作完成以后通过Handler切换到主线程中运行.这么以来我们无法管理我们所创建的子线程,并且无限制的创建子线程,它们相互之间竞争,很有可能由于占用过多资源而导致死机或者OOM.所以在Java中为我们提供了线程池来管理我们所创建的线程. 线程池的使用 采用线程池的好处 在这里我们首先来说一下采用线程池的

《Java并发编程的艺术》 第9章 Java中的线程池

第9章 Java中的线程池 在开发过程中,合理地使用线程池能带来3个好处: 降低资源消耗.通过重复利用已创建的线程 降低线程创建和销毁造成的消耗. 提高响应速度.当任务到达时,任务可以不需要等到线程创建就能立即执行. 提高线程的可管理性.线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配.调优和监控. 9.1 线程池的实现原理 当提交一个新任务到线程池时,线程池的处理流程如下: 1)线程池判断核心线程池里的线程是否都在执行任务.如果不是,则创建

如何创建Java中的线程池

线程是Java的一大特性,它可以是给定的指令序列.给定的方法中定义的变量或者一些共享数据(类一级的变量).在Java中每个线程有自己的堆栈和程序计数器(PC),其中堆栈是用来跟踪线程的上下文(上下文是当线程执行到某处时,当前的局部变量的值),而程序计数器则用来跟踪当前线程正在执行的指令. 一个线程不能访问另外一个线程的堆栈变量,而且这个线程必须处于如下状态之一: 1.排队状态(Ready),在用户创建了一个线程以后,这个线程不会立即运行.当线程中的方法start()被调用时,这个线程就会进行排队

Java中的线程池——ThreadPoolExecutor的原理

1 线程池的处理流程向线程池提交一个任务后,它的主要处理流程如下图所示一个线程从被提交(submit)到执行共经历以下流程: 线程池判断核心线程池里是的线程是否都在执行任务,如果不是,则创建一个新的工作线程来执行任务.如果核心线程池里的线程都在执行任务,则进入下一个流程线程池判断工作队列是否已满.如果工作队列没有满,则将新提交的任务储存在这个工作队列里.如果工作队列满了,则进入下一个流程.线程池判断其内部线程是否都处于工作状态.如果没有,则创建一个新的工作线程来执行任务.如果已满了,则交给饱和策

Java中的线程池ExecutorService

示例 import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; public class Ch09_Executor { private static void run(ExecutorService threadPool) { for(int i = 1; i < 5; i++)

java 中使用线程池处理文件夹下面的子文件

读取某个文件夹下面的所有文件,使用多线程处理,例如读取E盘下面的文件内容: package thread; import java.io.File; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue;

Java中的线程池模拟

老规矩,直接上代码,写的不好不要喷! public class ThreadExecutrols { public static void main(String[] args) { //创建一个线程池,固定数量为3 //ExecutorService ex = Executors.newFixedThreadPool(3); //创建单一的线程 //ExecutorService ex = Executors.newSingleThreadExecutor(); //利用缓存创建默认线程,在下

java笔记--使用线程池优化多线程编程

使用线程池优化多线程编程 认识线程池 在Java中,所有的对象都是需要通过new操作符来创建的,如果创建大量短生命周期的对象,将会使得整个程序的性能非常的低下.这种时候就需要用到了池的技术,比如数据库连接池,线程池等. 在java1.5之后,java自带了线程池,在util包下新增了concurrent包,这个包主要作用就是介绍java线程和线程池如何使用的. 在包java.util.concurrent下的 Executors类中定义了Executor.ExecutorService.Sche

Java并发之——线程池

一. 线程池介绍 1.1 简介 线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理.当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源. 多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力. 假设一个服务器完成一项