一、Callable和Future接口
1.介绍
Callable和Runnable都封装一个异步运行的任务:
Runnable:可以看成一个无参,无返回值的接口
Callable:有返回值的接口,里面有一个call()方法
Future :用来保存异步计算的结果,启动一个计算,将Future对象交给这个线程,忘掉它,Furture对象的所用者将会在得出结果后就可以得到这个值
2.FutureTask包装器:可以将Callable转换成Runnable,同时实现了这二中接口。
Callable<String> call=new Callable();
FutureTask<String >tack=new Future<String>(call); //将Callable转换成Runnable
Thread th=new Thread(tack); //创建线程
th.start(); //开启线程
String try=tack.get() //执行任务,调用阻塞,直到结束
2.1 执行器 Executor
- 为什么使用过线程池
1.构建一个新的线程是有一定代价的,因为沙及与操作系统的交互,
如果程序中创建了大量的生命期很短的线程,应该使用线程池(thread pool)。
一个线程池中包含许多准备运行的空闲线程。将Runnable对象交给线程池,就会有一个线程调用run方法。
当run方法退出时,线程不会死亡,而是在池中准备为下一个请求提供服务。
2.另一个使用线程池的理由是减少并发线程的数目。
创建大量线程会大大降低性能其至使虚拟机崩溃。
如果有一个会创建许多线程的算法,应该使用一个线程数“固定的“线程池以限制并发线程的总数。
- Executor接口介绍
1.一种执行器,提供管理终止的方法和可以产生Future用于跟踪一个或多个异步任务进度的方法。
2.接口方法 execute(Runnable command); //执行任务,无返回值
3.ExecutorServic 继承 Executor
2.2.Executors的静态工厂
1.new CacheThreadPool //必要时创建新线程,空闲是线程保留60秒
2.new FixedThreadPoll //包含固定的线程数量,线程会被一直保留
3.new SigneThreadExecutor //线程池中只有一个线程,会顺序的执行每一个提交的任务
4.new ScheduledThreadPool //预定执行的固定线程池,可以理解为定时发送任务。
5.new SingleThreadScheduledExecutor //单线程池
2.3 线程池
1.new CacheThreadPool //必要时创建新线程,空闲是线程保留60秒
2.new FixedThreadPoll //包含固定的线程数量,线程会被一直保留
3.new SigneThreadExecutor //线程池中只有一个线程,会顺序的执行每一个提交的任务
/**
这三和方法返回实现了ExecutorService接口的ThreadPollExcuteor类的对象
用下面三个方法来将Runnable或者Callable对象提交给ExecutorService,返回一个Future对象来查询任务的状态。
1.Future<?> submit(Runnable tack) get() 任务完成返回null
2.Future<T> submit(Callable<T> tack) Furture对象的所用者将会在得出结果后就可以得到这个值。
3.Future<T> submit(Runnable tack,T result) 使用get()方法是 任务完成返回指定的result对象
*/
2.3线程池的参数
ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
corePoolSize:核心线程数量
maximumPoolSize:最大线程数量;
workQueue:等待队列,当任务提交时,如果线程池中的线程数量大于等于corePoolSize的时候,把该任务封装成一个Worker对象放入等待队列;
keepAliveTime:线程池维护线程所允许的空闲时间。当线程池中的线程数量大于corePoolSize的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了keepAliveTime;
TimeUnit :时间级别
threadFactory:它是ThreadFactory类型的变量,用来创建新线程。
handler:它是RejectedExecutionHandler类型的变量,表示线程池的饱和策略。如果阻塞队列满了并且没有空闲的线程,这时如果继续提交任务,就需要采取一种策略处理该任务。
ThreadFactory:定义线程池中创建的线程,如线程名称,优先级等,可以几次ThreadFactory重新newThread(Runnable r)方法。
2.4线程池执行的基本流程
- 当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
- 当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
- 当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
- 当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
- 当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
- 当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭
2.5使用线程的大体步骤
- 调用Executors类的静态方法 例如 new CacheThreadPool
- 对象调用submit 提交Runnable或者Callable,用execute无返回值。
- 想取消任务,或者要提交Callable是要保存好返回的Future对象
- 不在想提交任务时,关闭将线程对象返回线程池用shutdown或者shutdownNew关闭所有;
package com.basic.test;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadTest implements Runnable
{
public static void main(String[] args) throws InterruptedException {
//创建一个定长为3的线程池
ExecutorService service = Executors.newFixedThreadPool(3);
//提交二个任务
service.submit(new ThreadTest());
service.submit(new ThreadTest());
//关闭线程
service.shutdown();
}
//要执行的任务
@Override
public void run() {
for (int i=0;i<10;i++)
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
执行结果部分:
pool-1-thread-2--->9
pool-1-thread-1--->3
pool-1-thread-1--->4
pool-1-thread-1--->5
pool-1-thread-1--->6
pool-1-thread-1--->7
pool-1-thread-1--->8
pool-1-thread-1--->9
参考链接
[https://www.jianshu.com/p/9beab78a3afe]: