线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。

用线程池来管理的好处是,可以保证系统稳定运行,适用与有大量线程,高工作量的情景下使用,假如要展示1000张图片如果创建1000个线程去加载,保证系统会死掉。用线程池就可以避免这个问题,可以用5个线程轮流执行,5个一组,执行完的线程不直接回收而是等待下次执行,这样对系统的开销就可以减小不少。

===============================Executor的译文部分==================================

Executor是Java工具类,执行提交给它的Runnable任务。该接口提供了一种基于任务运行机制的任务提交方法,包括线程使用详细信息,时序等等。Executor通常用于替代创建多线程。例如:你可能会使用以下方式来代替创建线程集合中的线程newThread(new(RunnableTask())).start()

 Executor executor = anExecutor;
executor.execute(new RunnableTask1());
executor.execute(new RunnableTask2());
...

尽管如此,Executor接口没有明确要求执行过程是异步的。举个最简单的例子,一个Executor可以在调用者的线程中运行提交的任务。

 class DirectExecutor implements Executor {
public void execute(Runnable r) {
r.run();
}
}

更典型的是,任务也可以运行在其他的线程而不是调用者线程。以下代码就是在Executor中生成新的线程。

class ThreadPerTaskExecutor implements Executor {
public void execute(Runnable r) {
new Thread(r).start();
}
}

很多Executor的实现按照任务的实现方式和时间来分类,下面的代码将提交的任务序列化给第二个Executor,阐述了一个组合的Executor。

class SerialExecutor implements Executor {
final Queue tasks = new ArrayDeque();
final Executor executor;
Runnable active; SerialExecutor(Executor executor) {
this.executor = executor; public synchronized void execute(final Runnable r) {
tasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (active == null) {
scheduleNext();
}
} protected synchronized void scheduleNext() {
if ((active = tasks.poll()) != null) {
executor.execute(active);
}
}
}}

以上代码简答讲就是执行一个SerialExecutor时,先执行Runnablerun(),然后再从Tasks任务堆栈中找到当前激活的任务并执行。

在这个package包中实现的Executor实现了ExecutorService,它是个扩展接口。

threadPoolExecutor类提供了一个扩展的线程池实现。Executors类给这些Executors提供了方便的工程方法。

内存一致性效果:在提交一个Runnable对象给Executor执行之前,线程中的行为可能先在另一个线程中发生。

===============================Executor的译文部分==================================

Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。

根据线程池的执行策略,Executorexecute()可能在新线程中执行,或者在线程池中的某个线程中执行,也可能是在调用者线程中执行。ExecutorServiceExecutor的基础上增加了两个核心方法:

1Future<?>submit(Runnable task)

2、<T>Future<T> submit(Callable<T> task)

差异点:这两个方法都可以向线程池提交任务,区别在于Runnable执行完run()有返回值,而Callable执行完call()后有返回值。

共同点:submit都返回Future对象,Future对象可以阻塞线程直到运行完毕,也可以取消任务执行和检测任务是否执行完毕。

executors类里面提供了一些静态工厂,生成一些常用的线程池:

1newSingleThreadExecutor创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

2newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

3newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

4newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

5newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。

下面再介绍下ThreadPoolExecutor类,以便对线程池有进一步认识:

ThreadPoolExecutor(int corePoolSize, 

                   int maximumPoolSize,
                   long keepAliveTime, 

                   TimeUnit unit,
                   BlockingQueue<Runnable> workQueue,
                   RejectedExecutionHandler handler)
corePoolSize   :线程池维护线程的最少数量
maximumPoolSize:线程池维护线程的最大数量
keepAliveTime  :线程池维护线程所允许的空闲时间
unit           :线程池维护线程所允许的空闲时间的单位
workQueue      :线程池所使用的缓冲队列
handler        :线程池对拒绝任务的处理策略

当一个任务通过execute(Runnable)方法欲添加到线程池时:

1如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
2
、如果此时线程池中的数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列。
3
、如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
4
、如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过handler所指定的策略来处理此任务。

也就是说,处理任务的优先级为:
核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。

简单的例子:

ThreadPoolTestMain.java

package threadpool.test;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; public class ThreadPoolTestMain {
private static final int CORE_POOL_SIZE = 2;
private static final int MAX_POOL_SIZE = 4;
private static final int KEEP_ACTIVE_TIME = 3;
private static final int TASK_NUM = 10;
private static final int PRODUCE_SLEEP_TIME = 10; static public void main(String[] args) {
// 构造一个线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ACTIVE_TIME,
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(3),
new ThreadPoolExecutor.DiscardOldestPolicy()); for (int i = 1; i < TASK_NUM; i++) {
String name = "Task" + i;
try {
System.out.println("ThreadPoolTestMain: put a task: " + name);
threadPool.execute(new ThreadPoolTask(name));
Thread.sleep(20);
} catch (Exception err) {
err.printStackTrace();
}
}
}
}

ThreadPoolTask.java

package threadpool.test;

public class ThreadPoolTask implements Runnable {
private String mTaskName;
private static int CONSUME_SLEEP_TIME = 2000; public ThreadPoolTask(String name) {
mTaskName = name;
} @Override
public void run() {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName());
System.out.println("ThreadPoolTask :" + mTaskName); try {
Thread.sleep(CONSUME_SLEEP_TIME);
} catch (Exception err) {
err.printStackTrace();
}
} }

执行结果:

ThreadPoolTestMain:put a task: Task1

pool-1-thread-1

ThreadPoolTask:Task1

ThreadPoolTestMain:put a task: Task2

pool-1-thread-2

ThreadPoolTask:Task2

ThreadPoolTestMain:put a task: Task3

ThreadPoolTestMain:put a task: Task4

ThreadPoolTestMain:put a task: Task5

ThreadPoolTestMain:put a task: Task6

pool-1-thread-3

ThreadPoolTask:Task6

ThreadPoolTestMain:put a task: Task7

pool-1-thread-4

ThreadPoolTask:Task7

ThreadPoolTestMain:put a task: Task8

ThreadPoolTestMain:put a task: Task9

pool-1-thread-1

ThreadPoolTask:Task5

pool-1-thread-2

ThreadPoolTask:Task8

pool-1-thread-3

ThreadPoolTask:Task9

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

  1. Java中线程池,你真的会用吗?

    在<深入源码分析Java线程池的实现原理>这篇文章中,我们介绍过了Java中线程池的常见用法以及基本原理. 在文中有这样一段描述: 可以通过Executors静态工厂构建线程池,但一般不建 ...

  2. 沉淀再出发:java中线程池解析

    沉淀再出发:java中线程池解析 一.前言 在多线程执行的环境之中,如果线程执行的时间短但是启动的线程又非常多,线程运转的时间基本上浪费在了创建和销毁上面,因此有没有一种方式能够让一个线程执行完自己的 ...

  3. Java中线程池,你真的会用吗?ExecutorService ThreadPoolExcutor

    原文:https://www.hollischuang.com/archives/2888 在<深入源码分析Java线程池的实现原理>这篇文章中,我们介绍过了Java中线程池的常见用法以及 ...

  4. java中线程池的使用方法

    1 引入线程池的原因 由于线程的生命周期中包括创建.就绪.运行.阻塞.销毁阶段,当我们待处理的任务数目较小时,我们可以自己创建几个线程来处理相应的任务,但当有大量的任务时,由于创建.销毁线程需要很大的 ...

  5. Java中线程池的实现原理

    知识点总结 ---------------------------------------------------------------------------------------------- ...

  6. Java中线程池的实现原理-求职必备

    jdk1.5引入Executor线程池框架,通过它把任务的提交和执行进行解耦,只需要定义好任务,然后提交给线程池,而不用关心该任务是如何执行.被哪个线程执行,以及什么时候执行. 初始化线程池(4种) ...

  7. JAVA中线程池的简单使用

    比如现在有10个线程,但每次只想运行3个线程,当这3个线程中的任何一个运行完后,第4个线程接着补上.这种情况可以使用线程池来解决,线程池用起来也相当的简单,不信,你看: package com.dem ...

  8. java中线程池的几种实现方式

    1.线程池简介:    多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力.        假设一个服务器完成一项任务所需时间为:T1 创建 ...

  9. 第九章 Java中线程池

    Java中的线程池是运用场景最多的并发框架,几乎所有需求异步或并发执行任务的程序都可以使用线程池.在开发过程中,合理地使用线程池能够带来3个好处. 降低资源消耗:通过重复利用已创建的线程降低线程创建和 ...

随机推荐

  1. jQuery扩展与noConflict的用法-小示例

    有时我们要用到自己定义的jquery,这时可以通过jQuery扩展来实现该功能 index.html <!DOCTYPE html> <html> <head> & ...

  2. ubuntu dash

    缘由:写一些脚本放在/etc/rc.loca自动执行开机启动报错 fiail to start /etc/rc.local 查找资料 缘起:ubuntu在6.0后默认bash改为了dash  导致很多 ...

  3. ajax返回json数组遍历添加到html

    大致需求为类型限制根据类型获取不同结果列表,再根据模糊查询搜索出结果,效果如下:

  4. Java里的接口

    Java里面由于不允许多重继承,所以如果要实现多个类的功能,则可以通过实现多个接口来实现. Java接口和Java抽象类代表的就是抽象类型,就是我们需要提出的抽象层的具体表现.OOP面向对象的编程,如 ...

  5. 关于 视频同步vsync 信号在不同一时候钟域採样问题

    今天调试 视频 4k(3840 x 1920)的vsync信号(时钟为 297Mhz) 进入 170Mhz 的时钟域, 发现输出来的信号信号抖动特别厉害.后来才发现这是不同一时候钟域 造成的影响. 快 ...

  6. hdu 4940 无源汇有上下界最大流

    /* <img src="http://img.blog.csdn.net/20140823174212937?watermark/2/text/aHR0cDovL2Jsb2cuY3N ...

  7. Linux内核如何启动并装载一个可执行程序

    2016-04-07 张超<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000#/info 一.理解编译链接的 ...

  8. mysql数据库的高可用方法总结

    高可用架构对于互联网服务基本是标配,无论是应用服务还是数据库服务都需要做到高可用.虽然互联网服务号称7*24小时不间断服务,但多多少少有一 些时候服务不可用,比如某些时候网页打不开,百度不能搜索或者无 ...

  9. .net程序开发人员必看的变量的命名规则

    (1)类名.属性名.方法名采用Pascal命名,如 class User { } interface IEditable { } bool ValidateInput() public int Age ...

  10. firefox 不能显示 glyphicons 字体

    折腾了很久才发现是firefox 不能跨域下载相应的字体文件,将bootstrap相应的css文件和字体文件copy到调用的项目里,问题才得以解决.