线程实现方式
继承Thread类创建线类
Thread本质上是实现了Runnable接口的一个实例实现Runnable接口
实现Runnable接口比继承Thread类所具有的优势:适合多个相同的程序代码的线程去处理同一个资源;可以避免java中的单继承的限制实现Callable接口:
适用需要线程返回的结果。就要使用用Callable、Future、FutureTask、CompletionService这几个类。Callable只能在ExecutorService的线程池中跑,但有返回结果,也可以通过返回的Future对象查询执行状态。
多线程管理
线程池
- Executors接口
提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。有两个重要的实现类。ThreadPoolExecutor:ExecutorService的默认实现;ScheduledThreadPoolExecutor:继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。
- 固定数目线程池。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- 优点:创建一个固定大小线程池,超出的线程会在队列中等待。
- 缺点:不支持自定义拒绝策略,大小固定,难以扩展
- 可缓存的线程池。调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- 优点:很灵活,弹性的线程池线程管理,用多少线程给多大的线程池,不用后及时回收,用则新建
- 缺点:一旦线程无限增长,会导致内存溢出。
- 单线程Executor。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
- 优点:创建一个单线程的线程池,保证线程的顺序执行
- 缺点:不适合并发。。不懂为什么这种操作要用线程池。。为什么不直接用队列
- 支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
}
- 优点:创建一个固定大小线程池,可以定时或周期性的执行任务。
- 缺点:任务是单线程方式执行,一旦一个任务失败其他任务也受影响
- ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。
从以上线程池的实现可以看出,本质上他们都是ThreadPoolExecutor类的各种实现版本。
ThreadPoolExecutor
public ThreadPoolExecutor(
int corePoolSize, - 池内线程初始值与最小值,就算是空闲状态,也会保持该数量线程。
int maximumPoolSize, - 线程池的最大线程数。
long keepAliveTime, - 当池内线程数高于corePoolSize时,经过多少时间多余的空闲线程才会被回收。回收前处于wait状态
TimeUnit unit, - keepAliveTime 时间单位,可以使用TimeUnit的实例,如TimeUnit.MILLISECONDS
BlockingQueue<Runnable> workQueue, - 用来储存等待执行任务的队列。
ThreadFactory threadFactory, - 线程工厂有默认实现,如果有自定义的需要则需要自己实现ThreadFactory接口并作为参数传入。
开发注意事项
- 线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors返回的线程池对象的弊端如下:
1)FixedThreadPool和SingleThreadPool:
允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
2)CachedThreadPool:
允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。