Java线程池总结和经常使用开源库的使用

new Thread的弊端

执行一个异步任务你还只是以下new Thread吗?java

new Thread(new Runnable() {

	@Override
	public void run() {
		// TODO Auto-generated method stub
	}
}).start();
复制代码

new Thread的弊端以下:数据库

  • 每次new Thread新建对象性能差。
  • 线程缺少统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源致使死机或oom。
  • 缺少更多功能,如定时执行、按期执行、线程中断。 相比new Thread,Java提供的四种线程池的好处在于:
  • 重用存在的线程,减小对象建立、消亡的开销,性能佳。
  • 可有效控制最大并发线程数,提升系统资源的使用率,同时避免过多资源竞争,避免堵塞。
  • 提供定时执行、按期执行、单线程、并发数控制等功能。

Java 线程池

Java经过Executors提供四种线程池,分别为:缓存

  • new CachedThreadPool 建立一个可缓存线程池,若是线程池长度超过处理须要,可灵活回收空闲线程,若无可回收,则新建线程。
  • new FixedThreadPool 建立一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  • new ScheduledThreadPool 建立一个定长线程池,支持定时及周期性任务执行。
  • new SingleThreadExecutor 建立一个单线程化的线程池,它只会用惟一的工做线程来执行任务,保证全部任务按照指定顺序(FIFO, LIFO, 优先级)执行。

Executors 主要是这个构造函数:安全

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

而Executors工厂类一共能够建立四种类型的线程池,经过Executors.newXXX便可建立。下面就分别都介绍一下把。网络

1. FixedThreadPool

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

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
	final int index = i;
	fixedThreadPool.execute(new Runnable() {
 
		@Override
		public void run() {
			try {
				System.out.println(index);
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	});
}
复制代码

由于线程池大小为3,每一个任务输出index后sleep 2秒,因此每两秒打印3个数字。异步

定长线程池的大小最好根据系统资源进行设置。ide

原理

public static ExecutorService newFixedThreadPool(int nThreads){
    return new ThreadPoolExecutor(nThreads,nThreads,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}
复制代码
  • 它是一种固定大小的线程池;
  • corePoolSize和maximunPoolSize都为用户设定的线程数量nThreads;
  • keepAliveTime为0,意味着一旦有多余的空闲线程,就会被当即中止掉;但这里keepAliveTime无效;
  • 阻塞队列采用了LinkedBlockingQueue,它是一个无界队列;
  • 因为阻塞队列是一个无界队列,所以永远不可能拒绝任务;- 因为采用了无界队列,实际线程数量将永远维持在nThreads,所以maximumPoolSize和keepAliveTime将无效。

2. CachedThreadPool

建立一个可缓存线程池,若是线程池长度超过处理须要,可灵活回收空闲线程,若无可回收,则新建线程。示例代码以下:函数

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
	final int index = i;
	try {
		Thread.sleep(index * 1000);
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
 
	cachedThreadPool.execute(new Runnable() {
 
		@Override
		public void run() {
			System.out.println(index);
		}
	});
}
复制代码
原理

//构造函数
public static ExecutorService newCachedThreadPool(){
    return new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.MILLISECONDS,new SynchronousQueue<Runnable>());
}
复制代码

线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。性能

  • 它是一个能够无限扩大的线程池;
  • 它比较适合处理执行时间比较小的任务;corePoolSize为0,maximumPoolSize为无限大,意味着线程数量能够无限大;
  • keepAliveTime为60S,意味着线程空闲时间超过60S就会被杀死;
  • 采用SynchronousQueue装等待的任务,这个阻塞队列没有存储空间,这意味着只要有请求到来,就必需要找到一条工做线程处理他,若是当前没有空闲的线程,那么就会再建立一条新的线程。

3. SingleThreadExecutor

建立一个单线程化的线程池,它只会用惟一的工做线程来执行任务,保证全部任务按照指定顺序(FIFO, LIFO, 优先级)执行。示例代码以下:

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
	final int index = i;
	singleThreadExecutor.execute(new Runnable() {

		@Override
		public void run() {
			try {
				System.out.println(index);
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	});
}
复制代码

结果依次输出,至关于顺序执行各个任务。

原理

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

它只会建立一条工做线程处理任务;采用的阻塞队列为LinkedBlockingQueue;

4. ScheduledThreadPool

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

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Runnable() {
 
	@Override
	public void run() {
		System.out.println("delay 3 seconds");
	}
}, 3, TimeUnit.SECONDS);
复制代码

表示延迟3秒执行。

scheduledThreadPool.scheduleAtFixedRate(new Runnable() {

	@Override
	public void run() {
		System.out.println("delay 1 seconds, and excute every 3 seconds");
	}
}, 1, 3, TimeUnit.SECONDS);
复制代码

表示延迟1秒后每3秒执行一次。

ScheduledExecutorService比Timer更安全,功能更强大

原理

  • 它接收SchduledFutureTask类型的任务,有两种提交任务的方式:scheduledAtFixedRate, scheduledWithFixedDelay
  • 它采用DelayQueue存储等待的任务

DelayQueue内部封装了一个PriorityQueue,它会根据time的前后时间排序,若time相同则根据sequenceNumber排序;

DelayQueue也是一个无界队列;

工做线程会从DelayQueue取已经到期的任务去执行;

执行结束后从新设置任务的到期时间,再次放回DelayQueue

最佳实践:

AsynTask 4.0

  • 默认线程池是 SingleThreadExecutor; 单一串行线程池。
  • 内部还内置一个CPU*2+1的 FixedThreadPool,命名为:THREAD_POOL_EXECUTOR。

RxJava

  • Schedulers.io( )

默认是一个CachedThreadScheduler,用于IO密集型任务,如异步阻塞IO操做(读写文件、读写数据库、网络信息交互等)所使用的 Scheduler。

  • Schedulers.computation( )

默认是:FixedThreadPool。用于CPU 密集型计算,即不会被 I/O 等操做限制性能的操做,例如图形的计算,事件循环或和回调处理

  • Schedulers.immediate( )

在当前线程当即开始执行任务,至关于不指定线程。这是默认的 Scheduler

  • AndroidSchedulers.mainThread()

Android专用的,指定的操做将在 Android 主线程运行

  • Schedulers.from(executor)

使用指定的Executor做为调度器

  • Schedulers.newThread()

老是启用新线程,并在新线程执行操做。

EventBus:

  • 使用 newCachedThreadPool,实现 前台线程通知

OKHTTP:

  • 相似 newCachedThreadPool 实现:
executor = new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue(), Util.threadFactory("OkHttp ConnectionPool", true));
复制代码

Fresco

  • Default implementation of SerialExecutorService

参考

  1. 代码例子参考网络
  2. 图片流程图部分来自知乎截图
相关文章
相关标签/搜索