最近一直在刷面试题,刷到“如何创建进程”,所以记录下来
进程一直是面试的热点问题,创建进程的方式有四种,分别是继承Thread类创建线程、实现Runnable接口创建线程、使用Callable和Future创建线程、使用线程池例如用Executor框架。
1 继承Thread类创建线程
通过继承Thread类来创建并启动多线程,其代码如下:
public class MyThread extends Thread{//继承Thread类
public void run(){
//重写run方法
}
}
public class Main {
public static void main(String[] args){
new MyThread().start();//创建并启动线程
}
}
2 实现Runnable接口创建线程
通过实现Runnable接口创建并启动线程,其代码如下:
public class MyThread2 implements Runnable {//实现Runnable接口
public void run(){
//重写run方法
}
}
public class Main {
public static void main(String[] args){
//创建并启动线程
MyThread2 myThread=new MyThread2();
Thread thread=new Thread(myThread);
thread().start();
//或者 new Thread(new MyThread2()).start();
}
}
3 使用Callable和Future创建线程
和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大。call()方法可以有返回值;call()方法可以声明抛出异常。相关代码如下:
public class ThreadDemo implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
/**
* 求得0到100000的自然数相加,并输出过程值。
*/
for (int i = 0; i <= 100000; i++) {
System.out.println(i);
sum += i;
}
return sum;
}
}
public class Main {
public static void main(String[] args){
ThreadDemo threadDemo =new ThreadDemo();
// 1.执行Callable方式,需要FutureTask实现类的支持,用于接收运算结果
FutureTask<Integer> task = new FutureTask<Integer>(threadDemo);
new Thread(task).start();
// 2.接收线程运算后的结果
Integer sum;
try {
//等所有线程执行完,获取值,因此FutureTask 可用于 闭锁
sum = task.get();
System.out.println("-----------------------------");
System.out.println(sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
4 使用线程池例如用Executor框架
1.5后引入的Executor框架的最大优点是把任务的提交和执行解耦。要执行任务的人只需把Task描述清楚,然后提交即可。
4.1 Executor执行Runnable任务
通过Executors的以上四个静态工厂方法获得 ExecutorService实例,而后调用该实例的execute(Runnable command)方法即可。一旦Runnable任务传递到execute()方法,该方法便会自动在一个线程上,相关代码如下:
public class TestRunnable implements Runnable{
@Override
public void run() {
System.out.println("第四种创建线程的方式");
}
}
public class Main {
public static void main(String[] args){
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++){
// 接收Runnable实例
executorService.execute(new TestRunnable());
System.out.println("************* a" + i + " *************");
}
// 平滑关闭ExecutorService
executorService.shutdown();
}
}
4.2 Executor执行Callable任务
在Java 5之后,任务分两类:一类是实现了Runnable接口的类,一类是实现了Callable接口的类。两者都可以被ExecutorService执行,但是Runnable任务没有返回值,而Callable任务有返回值。并且Callable的call()方法只能通过ExecutorService的submit(Callable task) 方法来执行,并且返回一个 Future,是表示任务等待完成的 Future。相关代码如下:
public class TaskWithResult implements Callable<String> {
private int id;
/**
* 构造方法
* @param id
*/
public TaskWithResult(int id){
this.id = id;
}
/**
* 任务的具体过程,一旦任务传给ExecutorService的submit方法,
* 则该方法自动在一个线程上执行
*/
@Override
public String call() throws Exception {
System.out.println("call()方法被自动调用!!! " + Thread.currentThread().getName());
//该返回结果将被Future的get方法得到
return "call()方法被自动调用,任务返回的结果是:" + id + " " + Thread.currentThread().getName();
}
}
public class Main {
public static void main(String[] args){
ExecutorService executorService = Executors.newCachedThreadPool();
List<Future<String>> resultList = new ArrayList<Future<String>>();
// 创建10个任务并执行
for (int i = 0; i < 10; i++){
//使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中
Future<String> future = executorService.submit(new TaskWithResult(i));
//将任务执行结果存储到List中
resultList.add(future);
}
//遍历任务的结果
for (Future<String> fs : resultList){
try{
while(!fs.isDone()) {
;//Future返回如果没有完成,则一直循环等待,直到Future返回完成
}
//打印各个线程(任务)执行的结果
System.out.println(fs.get());
}catch(InterruptedException e){
e.printStackTrace();
}catch(ExecutionException e){
e.printStackTrace();
}finally{
//启动一次顺序关闭,执行以前提交的任务,但不接受新任务
executorService.shutdown();
}
}
}
}
总结
以上方法的代码都可以运行,并能执行成功,前三种方式理解起来非常简单,最后一种,要理解就必须看一下Executor框架知识。链接如下