Java中线程的创建方式与线程池的使用
前言
今天做了一家公司的笔试题,结果感觉不太好。题目大多数是基础题,很少涉及到框架相关的内容,果然对实习生来说还是基础知识重要点= =。最后两道是手写编程题,一道要求手写单例模式、另一道jvm内存回收。设计模式前几天刚复习过,最后写出来感觉还行,但第二道就难受了,jvm虚拟机部分几乎没怎么涉及过,就空了交卷。接下来要好好复习基础。回到正题,今天要说的是线程的创建方式和线程池的使用。
线程的三种创建方式
- 实现Runnable接口
- 实现Callable接口
- 继承Thread类
实现 Runnable 和 Callable 接口的类只能当做一个可以在线程中运行的任务,不是真正意义上的线程,因此最后还需要通过 Thread 来调用。可以理解为任务是通过线程驱动从而执行的。
- 实现Runnable接口并实现
run()
方法public class RunnableImpl implements Runnable{
public void run() {
System.out.println("runnable:run");
}
} - 实现Callable接口并实现
call()
方法//call返回值通过FutureTask封装,使用FutureTask.get()方法获得返回值
public class CallableImpl implements Callable<String> {
public String call() {
return "callable: call";
}
} - 继承Thread类并重写
run()
方法public class ThreadExtend extends Thread{
public void run() {
super.run();
System.out.println("thread: run");
}
} - 三种创建线程的方式一一通过
start()
执行
public void TestCreateThread() throws ExecutionException, InterruptedException {
//实现runnable接口
Runnable runnable=new RunnableImpl();
Thread thread1=new Thread(runnable);
thread1.start();
//实现callable接口
CallableImpl callable = new CallableImpl();
FutureTask<String> futureTask = new FutureTask<>(callable);
Thread thread2=new Thread(futureTask);
thread2.start();
System.out.println(futureTask.get());
//thread继承
Thread thread3=new ThreadExtend();
thread3.start();
}
注意,线程开启要使用start()
方法,而不是使用run()
。start()
是启动一个线程,此时的线程处于就绪状态,等时间片到即开始运行。run()
并没有开辟一个线程,仅仅是在当前的线程中执行run()
方法。
线程池的使用
线程池实现类的顶层接口是Executor
,顶层接口Executor
提供了一种思想:将任务提交和任务执行进行解耦。开发者无需关注如何创建线程,如何调度线程来执行任务,开发者只需提供Runnable
对象,将任务的运行逻辑提交到执行器Executor
中,由Executor
框架完成线程的调配和任务的执行部分。
ExecutorService
接口增加了一些能力:(1)扩充执行任务的能力,补充可以为一个或一批异步任务生成Future
的方法;(2)提供了管控线程池的方法,比如停止线程池的运行。AbstractExecutorService
则是上层的抽象类,将执行任务的流程串联了起来,保证下层的实现只需关注一个执行任务的方法即可。- 最下层的实现类
ThreadPoolExecutor
实现最复杂的运行部分,ThreadPoolExecutor
将会一方面维护自身的生命周期,另一方面同时管理线程和任务,使两者良好的结合从而执行并行任务。
主要的Executor
有三种:CachedThreadPool
、FixedThreadPool
、SingleThreadExecutor
,都可通过Executors
获得,返回值是ExecutorService
。
- CachedThreadPool:一个任务创建一个线程;
- FixedThreadPool:所有任务只能使用固定大小的线程;
- SingleThreadExecutor:相当于大小为 1 的 FixedThreadPool。
线程池的实现:
|
通常我们使用多线程都是进行一些并发的操作,如下载等。那么我们想要记录一下线程执行结果可以通过ExecutorService
的submit()
提交任务后的返回值来获得,或者通过CompletionService
来对任务执行结果进行处理。CompletionService
中take()
可获得Future
对象,根据其get()
方法我们可以获得线程返回值。take()
方法返回的是最早执行完毕的任务结果,所以在下载记录进度的这种情况下我们使用CompletionService
会更加的方便
使用CompletionService
对线程池返回值进行管理:
|