(2)Java Executor框架
参考:
Java Executor框架
Executor框架是指java5中引入的一系列并发库中与executor相关的功能类,包括Executor、Executors、ExecutorService、CompletionService、Future、Callable等。通过学习Executor框架能够更加清楚明晰作者的写作思路和来源。
Class Executor
开始的开始 Executor
public interface Executor {
void execute(Runnable command);
}
Executor接口是Executor框架中最基础的部分,定义了一个用于执行Runnable的execute方法。它没有直接的实现类,有一个重要的子接口ExecutorService。
ExecutorService
ExecutorService接口继承自Executor接口,定义了终止、提交任务、跟踪任务返回结果等方法。
Runnable、Callable、Future
// 实现Runnable接口的类将被Thread执行,表示一个基本的任务
public interface Runnable {
// run方法就是它所有的内容,就是实际执行的任务
public abstract void run();
}
// Callable同样是任务,与Runnable接口的区别在于它接收泛型,同时它执行任务后带有返回内容
public interface Callable<V> {
// 相对于run方法的带有返回值的call方法
V call() throws Exception;
}
// Future代表异步任务的执行结果
public interface Future<V> {
/**
* 尝试取消一个任务,如果这个任务不能被取消(通常是因为已经执行完了),返回false,否则返回true。
*/
boolean cancel(boolean mayInterruptIfRunning);
/**
* 返回代表的任务是否在完成之前被取消了
*/
boolean isCancelled();
/**
* 如果任务已经完成,返回true
*/
boolean isDone();
/**
* 获取异步任务的执行结果(如果任务没执行完将等待)
*/
V get() throws InterruptedException, ExecutionException;
/**
* 获取异步任务的执行结果(有最常等待时间的限制)
*
* timeout表示等待的时间,unit是它时间单位
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
ScheduledFuture继承自Future和Delayed接口,自身没有添加方法。Delayed接口定义了一个获取剩余延迟的方法。
ExecutorService有一个子接口ScheduledExecutorService和一个抽象实现类AbstractExecutorService。
ScheduledExecutorService
可以安排指定时间或周期性的执行任务的ExecutorService
// 可以安排指定时间或周期性的执行任务的ExecutorService
public interface ScheduledExecutorService extends ExecutorService {
/**
* 在指定延迟后执行一个任务,只执行一次
*/
public ScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit);
/**
* 与上面的方法相同,只是接受的是Callable任务
*/
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay, TimeUnit unit);
/**
* 创建并执行一个周期性的任务,在initialDelay延迟后每间隔period个单位执行一次,时间单位都是unit
* 每次执行任务的时间点是initialDelay, initialDelay+period, initialDelay + 2 * period...
*/
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
/**
* 创建并执行一个周期性的任务,在initialDelay延迟后开始执行,在执行结束后再延迟delay个单位开始执行下一次任务,时间单位都是unit
* 每次执行任务的时间点是initialDelay, initialDelay+(任务运行时间+delay), initialDelay + 2 * (任务运行时间+delay)...
*/
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
}
AbstractExecutorService
看到AbstractExecutorService的时候,基本就能找到一点痕迹了,LiteGo的核心代码有一些都参照了它的写法。
我们先来了解下下面三个概念:
- RunnableFuture继承自Future和Runnable,只有一个run()方法。RunnableFuture接口看上去就像是Future和Runnable两个接口的组合。
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
- FutureTask类实现了RunnableFuture接口,除了实现了Future和Runnable中的方法外,它还有自己的方法。
- ExecutorCompletionService实现了CompletionService接口,将结果从复杂的一部分物种解耦出来。
ThreadPoolExecutor
ThreadPoolExecutor继承自AbstractExecutorService。
ScheduledThreadPoolExecutor
它继承自ThreadPoolExecutor并实现了ScheduledExecutorService接口。
Executors
Executors中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory
和 Callable
类的工厂和实用方法。
- newFixedThreadPool
创建一个定长的线程池。达到最大线程数后,线程数不再增长。
如果一个线程由于非预期Exception而结束,线程池会补充一个新的线程。 - newCachedThreadPool
创建一个可缓存的线程池。当池长度超过处理需求时,可以回收空闲的线程。 - newSingleThreadPool
创建一个单线程executor。 - newScheduledThreadPool
创建一个定长的线程池,而且支持定时的以及周期性的任务执行。
类似于Timer。但是,Timer是基于绝对时间,对系统时钟的改变是敏感的,而ScheduledThreadPoolExecutor只支持相对时间。
与Timer比较
- Timer是创建唯一的线程来执行所有的timer任务。如果一个任务超时了,会导致其他的TimerTask时间准确性出问题。
- 如果TimerTask抛出uncheck 异常,Timer将会产生无法预料的行为。因此,ScheduledThreadPoolExecutor可以完全代替Timer。
(3)LinkedList 和 ArrayList
参考链接
因为平时我们大多使用的是动态数组ArrayList,对LinkedList并不是那么的熟悉,这里探究一下它们的区别和源码中为什么要使用LinkedList。
LinkedList和ArrayList都实现了List接口,但是它们的工作原理却不一样。它们之间最主要的区别在于ArrayList是可改变大小的数组,而LinkedList是双向链接串列(doubly LinkedList)。
- 因为Array是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的。Array获取数据的时间复杂度是O(1),但是要删除数据却是开销很大的,因为这需要重排数组中的所有数据。
- 相对于ArrayList,LinkedList插入是更快的。因为LinkedList不像ArrayList一样,不需要改变数组的大小,也不需要在数组装满的时候要将所有的数据重新装入一个新的数组,这是ArrayList最坏的一种情况,时间复杂度是O(n),而LinkedList中插入或删除的时间复杂度仅为O(1)。ArrayList在插入数据时还需要更新索引(除了插入数组的尾部)。
- 类似于插入数据,删除数据时,LinkedList也优于ArrayList。
- LinkedList需要更多的内存,因为ArrayList的每个索引的位置是实际的数据,而LinkedList中的每个节点中存储的是实际的数据和前后节点的位置。
在LinkedList中有一个私有的内部类,定义如下:
private static class Entry {
Object element;
Entry next;
Entry previous;
}
这也就是LinkedList耗费内存更多的地方。
什么场景下更适宜使用LinkedList,而不用ArrayList
- 你的应用不会随机访问数据。因为如果你需要LinkedList中的第n个元素的时候,你需要从第一个元素顺序数到第n个数据,然后读取数据。
- 你的应用更多的插入和删除元素,更少的读取数据。因为插入和删除元素不涉及重排数据,所以它要比ArrayList要快。
以上就是关于ArrayList和LinkedList的差别。你需要一个不同步的基于索引的数据访问时,请尽量使用ArrayList。ArrayList很快,也很容易使用。但是要记得要给定一个合适的初始大小,尽可能的减少更改数组的大小。
由此可见,LiteGo的使用场景中可能会出现大量的任务插入和删除(插入和删除有可能发生在列表的前面),但并不会涉及到查询,所以这里使用LinkedList是有道理的。
网友评论