线程池的优缺点
线程池工具的必要性
* 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
* 使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。
* 如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
以下情况不建议使用线程池
*线程任务耗时时间比较长,或者需要有专门的线程长期存在来支持任务
*线程数量不多,或者不会频繁上下文切换,不需要频繁创建线程
java线程池实现ThreadPoolExecutor和Executors
可以通过以下方式获取线程池
/**
* corePoolSize 核心线程数,线程池中保持的线程数,即使他们是空闲的,除非设置了超时时间
* maximumPoolSize 线程池中允许的最大线程数
* keepAliveTime 当线程总数多于核心线程数时,这是被终止前的最大空闲时间
* unit keepAliveTime 时间单位
* workQueue 阻塞队列,当线程池正在运行的线程数量已经达到corePoolSize,那么再通过execute添加新的任*务则会被加到workQueue队列中,在队列中排队等待执行,而不会立即执行。
* threadFactory 产生线程的线程工厂
new ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
//创建固定数量线程的线程池(实际上会为新的任务启动新的线程,维持固定的线程数)
Executors.newFixedThreadPool(int nThreads)
//创建可重复利用空闲线程的线程池,适用于执行执行许多短期异步任务
Executors.newCachedThreadPool()
//单线程线程池,所有提交的任务按照顺序执行
Executors.newSingleThreadExecutor()
//周期执行的线程池(完成周期性的任务)
Executors.newScheduledThreadPool ()
Executors提供了一些封装好的线程池来满足不同的需要,但是生产中不建议直接使用,而是采用ThreadPoolExecutor类自己配置,明确配置条件.
Executors提供的线程池参数中
FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致OOM。
CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。
Thread类
java中创建Thread的方法
1)继承Thread类创建线程
2)实现Runnable接口创建线程
3)使用Callable和Future创建线程
使用future获取异步结果/执行异常
如果需要获取其他线程的结果,一般采用回调的形式,但是这个回调,无法保证执行顺序,一旦多线程因为异常中断,没有执行回调,也就无法获知执行结果成功还是失败
java提供了Future类来获取异步执行结果和中断原因
Future是一个接口,接口方法
//取消线程任务
//mayInterruptIfRunning 参数是否允许任务中断.true,允许中断
//返回值,ture,成功中断;false,中断失败,通常是由于任务已经完成了
boolean cancel(boolean mayInterruptIfRunning);
//检查任务是否取消
boolean isCancelled();
//检查任务是否完成
boolean isDone();
//获取任务结果,可以抛出异常
V get() throws InterruptedException, ExecutionException;
//获取任务结果的重载,添加了超时时间
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
如何获取Future对象
通过ThreadPoolExecutor.submit()方法,在提交Callable对象时future对象
调用future.get方法可以获取Callable的执行结果,调用get()的线程会阻塞等待结果的返回
public static void main(String[] args) {
System.out.println("submit callable");
System.out.println("submit now="+System.currentTimeMillis());
System.out.println("submit thread="+Thread.currentThread().getId());
Future<Integer> future=Executors.newSingleThreadExecutor().submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("callable call");
System.out.println("callable now="+System.currentTimeMillis());
System.out.println("callable thread="+Thread.currentThread().getId());
Thread.sleep(5000);
return 1000;
}
});
try {
System.out.println("before future get");
System.out.println("before now="+System.currentTimeMillis());
System.out.println("before thread="+Thread.currentThread().getId());
int result=future.get();
System.out.println("after future get");
System.out.println("after now="+System.currentTimeMillis());
System.out.println("after thread="+Thread.currentThread().getId());
System.out.println("result="+result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
不同线程池的区别
*线程池内部维护的线程数,维护方式,参考线程池参数中的线程数目,以及新线程的获取
*线程接受的任务存放的方式和调度的方式,参考线程池参数中的任务队列