java线程池概述

         池的概念不限于java语言,但是在java编程中我们经常会遇到各种池,数据库连接池(mysql,oracle,redis,mongodb,memcache),几乎没有web项目不使用到连接池的,再一个就是今天要说的线程池,在多线程编程中,线程需要不断的创建和销毁,这笔开销是很大的,如果控制不好,容易导致系统瘫痪。线程池技术,解决了多线程编程线程的创建,销毁的开销问题。

         以上其实说明了线程池的使用场景:第一、单个任务执行时间短,很快完成,第二、任务量大。

         线程池的思路:构建一些可用线程供使用,无需反复创建,使用完了归还,别的任务接着使用,在任务多的情况下他使用一个队列来做缓冲区。其实这个队列中保存的就是等待执行的任务线程,因此我们很容易理解BlockingQueue<Runnable>的含义,线程池示例如下所示:

package com.xxx.threadpool;
import java.util.Scanner;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExecutorMain {
	
	private ThreadPoolExecutorMain(){}
	private static CompletionService<Object> service;
	private static LinkedBlockingQueue<Runnable> queue;
	
	static{
		queue = new LinkedBlockingQueue<Runnable>();
		ExecutorService executorService = new ThreadPoolExecutor(1,3,60L,TimeUnit.SECONDS,queue);
		service = new ExecutorCompletionService<Object>(executorService);
	}
	
	
	public static Future<Object> execute(String taskName){
		return service.submit(new Callable<Object>() {			
			@Override
			public Object call() throws Exception {
				System.out.println("run for task : "+taskName);
				return "task-"+taskName;
			}
		});
	}

	@SuppressWarnings("resource")
	public static void main(String[] args) {
		String input = null;
		while(true){
			input = new Scanner(System.in).nextLine();
			if("exit".equals(input))
				break;
			execute(input);
		}
	}

}

执行结果:

这个示例无法看出线程池的作用,几乎和普通的Thread或者实现了Runnable接口的线程没什么区别,只是感受一下线程池的创建和执行任务的过程。

下面介绍线程池的类型,java线程池api为我们提供了使用Executors来创建线程池,通过它可以创建以下类型的线程池。

线程池的类型:

1、fixedThreadPool,线程池中可用线程数目固定,超过数目的任务等待有可用线程时执行,在空闲时不会释放资源。

2、cachedThreadPool,线程数无限制,可以创建从0到Integer.MAX_VALUE的线程,在空闲时可以释放资源,但是如果后续又有任务来,又需要重新创建线程。

3、singleThreadPool,保证线程池中始终只有一个可用线程,如果线程异常结束,则会有另一个线程被创建,可以保证任务的顺序执行,在任意时间不会有多余的活动线程。

4、scheduledThreadPool,可以指定线程数量,他的应用场景是执行定时任务或者需要重复执行的任务。

      通过以下部分源码可以看到:以上线程池的实现其实都是通过ThreadPoolExecutor来实现的,scheduledThreadPool更是实现了ScheduledExecutorService接口。  

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
public class ScheduledThreadPoolExecutor
        extends ThreadPoolExecutor
        implements ScheduledExecutorService

      通过一个示例来说明线程池之间的区别:创建4个任务,分别执行4次打印信息,每次打印之前sleep 100ms,而我们创建的线程池个数可以设定为3(cachedThreadPool和singleThreadPool无须设置),程序代码如下:

package com.xxx.threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ThreadPoolMain {

	private static void run(ExecutorService threadPool){
		for(int i=1;i<5;i++){
			final int taskId = i;
			threadPool.execute(new Runnable() {			
				@Override
				public void run() {
					for (int n = 1; n < 5; n++) {
						try {
							TimeUnit.MILLISECONDS.sleep(100);
						} catch (Exception e) {
							e.printStackTrace();
						}
						System.out.println("第"+taskId+"次任务,第"+n+"次执行");
					}					
				}
			});
		}
		threadPool.shutdown();
	}
	public static void main(String[] args) {
		ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
		ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
		ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
		ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
		run(fixedThreadPool);//先执行前面的三个任务,最后执行第四个任务
		run(cachedThreadPool);//四个任务几乎同时执行
		run(singleThreadPool);//四个任务按照顺序执行,线程池中只有一个线程可用,只能依次执行每一个任务
		run(scheduledThreadPool);//此种场景下和第一种线程池执行结果类似,先执行前面三个任务,最后执行第四个任务
	}

}

每次只运行一个线程池查看打印信息,分别如下图所示:

      在实际编程中,我们如何选择哪种线程池,其实不用过多的考虑,一般选择newFixedThreadPool(n),至于线程池中线程的个数,根据系统情况而定,一般为了充分利用系统cpu,编码中采用Runtime.getRuntime().availableProcessors()设置线程池的个数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luffy5459

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值