线程
java.lang.Thread/Runnable
- 一般创建线程只有两种方式,一种是继承Thread,一种是实现Runnable接口。但是这两种创建方式没有返回值
- 一般不提倡使用继承Thread来创建线程方式,因为Java只有单继承,不能继承多个。
- 之后要上线程池,如果是用Runnable来实现的,那就可以直接传入Runnable给线程池进行管理了
Callable
Callable也是一个接口,很简单就一个call方法返回的就是执行的结果了。和Runnable 差别就是它有返回的结果,并且可以抛出异常!
public interface Callable<V> {
V call() throws Exception;
}
配合ThreadPoolExecutor使用的, 不能直接配合Thread 使用。
Callable<String> callable =
new Callable<String>() {
@Override
public String call() throws Exception {
return "hello";
}
};
ExecutorService executor = Executors.newFixedThreadPool(1);
Future<String> result = executor.submit(callable);
System.out.println(result.get());
Future
Future也是一个接口,它可以对具体的Runnable或者Callable任务进行取消、判断任务是否已取消、查询任务是否完成、获取任务结果。 如果是Runnable的话返回的结果是null。
接口里面有以下几个方法。注意两个get方法都会阻塞当前调用get的线程,直到返回结果或者超时才会唤醒当前的线程。
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning); // 取消任务
boolean isCancelled(); // 判断任务是否已取消
boolean isDone(); // 判断任务是否已结束
V get() throws InterruptedException, ExecutionException;// 获得任务执行结果
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;// 获得任务执行结果,支持超时
}
FutureTask
因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。
FutureTask是个class。它实现了RunnableFuture接口public class FutureTask<V> implements RunnableFuture<V> 而RunnableFuture接口又继承了Runnable和Future
因此它可以作为Runnable被线程执行,又可以有Future的那些操作。
FutureTask<String> futureTask = new FutureTask<>(() -> "hello");
Thread T1 = new Thread(futureTask);
T1.start();
String result = futureTask.get();
System.out.println(result);
线程池
线程池的本质是对任务和线程的管理,而做到这一点最关键的思想就是将任务和线程两者解耦,不让两者直接关联,才可以做后续的分配工作。线程池中是以生产者消费者模式,通过一个阻塞队列来实现的。阻塞队列缓存任务,工作线程从阻塞队列中获取任务。
阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。
形象比喻:
执行流程:
handler 线程池的饱和策略:
- AbortPolicy 直接抛出异常,默认策略;
- CallerRunsPolicy 用调用者所在的线程来执行任务;
- DiscardOldestPolicy 丢弃阻塞队列中第一个(即最旧)的任务,并执行当前任务;
- DiscardPolicy 直接丢弃任务。
线程池不建议使用工具类Executors创建
线程池不允许使用工具类Executors创建,而是通过ThreadPoolExecutor的方式,这样的处理方式可以更加明确线程池的运行规则,规避资源耗尽的风险 Executors返回的线程池弊端如下:
- newFixedThreadPool和:newSingleThreadExecutor
允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM
- newCachedThreadPool 允许的创建线程数量为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM
final static ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());