併發編程中,線程創建的是很常見的,創建線程的方式也分幾種,一般創建線程有如下四種方式:
1.繼承Thread類;
2.實現Runnable接口;
3.實現Callable接口,結合FutureTask使用;
4.利用線程池ExecutorService,Callable,Future實現;
本文將分2部分來講解:
1. 實例演示4種方式的實現;
1. 對比分析4種方式的區別以及適用場景;
實例演示
- 繼承Thread類
實現邏輯如下:
/**
* 繼承Thead類
* 目標:創建2個線程併發實現從1-100的累加
* Created by greek on 2018/5/6.
*/
public class CountThread01 extends Thread {
private int start,end;
private int num=0;
public CountThread01(String name, int start, int end){
super(name);
this.start=start;
this.end=end;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"執行");
for (int i =start; i <=end; i++) {
num+=i;
}
System.out.println(Thread.currentThread().getName()+"執行結束,num="+num);
}
public static void main(String[] args) throws InterruptedException {
int start=0,mid=50,end=100;
CountThread01 t1=new CountThread01("線程1",start,mid);
CountThread01 t2=new CountThread01("線程2",mid+1,end);
t1.start();
t2.start();
//合併2個線程
t1.join();
t2.join();
System.out.println("結果:"+(int)(t1.num+t2.num));
}
}
實現步驟如下:
1. 繼承 Thread 類
1. 覆蓋 run() 方法
1. 直接調用 Thread#start() 執行
- 實現Runnable接口方式
實現邏輯如下:
/**
* 實現Runnable
* Created by greek on 2018/5/6.
*/
public class CountThread02 implements Runnable {
private int start,end;
private int num=0;
public CountThread02(int start,int end){
this.start=start;
this.end=end;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"執行");
for (int i =start; i <=end; i++) {
num+=i;
}
System.out.println(Thread.currentThread().getName()+"執行結束,sum="+num);
}
public static void main(String[] args) throws InterruptedException {
int start=0,mid=50,end=100;
CountThread02 count1=new CountThread02(start,mid);
CountThread02 count2=new CountThread02(mid+1,end);
Thread t1=new Thread(count1,"線程1");
Thread t2=new Thread(count2,"線程2");
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("結果:"+(int)(count1.num+count2.num));
}
}
實現步驟如下:
1. 實現Runnable接口;
1. 執行Runnable實例,作爲入參,創建Thread實例;
1. 執行Thread#start方法
- 實現Callable接口,結合FutureTask使用
實現邏輯如下:
/**
* 實現Callable接口,結合FutureTask使用
* Created by greek on 2018/5/6.
*/
public class CountThread03 implements Callable<Integer> {
private int start,end;
private int num=0;
public CountThread03(int start,int end){
this.start=start;
this.end=end;
}
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"執行");
for (int i =start; i <=end; i++) {
num+=i;
}
System.out.println(Thread.currentThread().getName()+"執行結束,num="+num);
return num;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
int start=0,mid=50,end=100;
int sum1=0,sum2=0;
FutureTask<Integer> task1=new FutureTask<Integer>(new CountThread03(start,mid));
FutureTask<Integer> task2=new FutureTask<Integer>(new CountThread03(mid+1,end));
Thread t1=new Thread(task1,"線程1");
Thread t2=new Thread(task2,"線程2");
t1.start();
t2.start();
t1.join();
t2.join();
if(task1.isDone()){
sum1=task1.get();
}
if(task1.isDone()){
sum2=task2.get();
}
System.out.println("結果:"+(int)(sum1+sum2));
}
}
實現步驟如下:
1. 實現Callable接口;
1. 以Callable的實現類爲參數,創建FutureTask實例;
1. 將FutureTask作爲Thread的參數,創建Thread實例;
1. 通過 Thread#start 啓動線程;
1. 通過 FutreTask#get() 阻塞獲取線程的返回值
- 線程池方式創建
實現邏輯如下:
/**
* 線程池方式
* Created by greek on 2018/5/6.
*/
public class CountThread04 implements Callable<Integer>{
private int start,end;
private int num=0;
public CountThread04(int start, int end){
this.start=start;
this.end=end;
}
@Override
public Integer call() throws Exception{
System.out.println(Thread.currentThread().getName()+"執行");
for (int i =start; i <=end; i++) {
num+=i;
}
System.out.println(Thread.currentThread().getName()+"執行結束,num="+num);
return num;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
int start=0,mid=50,end=100;
ExecutorService executorService= Executors.newFixedThreadPool(2);
Future<Integer> task1 = executorService.submit(new CountThread04(start,mid));
Future<Integer> task2 =executorService.submit(new CountThread04(mid+1,end));
System.out.println("結果:"+(int)(task1.get()+task2.get()));
}
}
實現步驟如下:
1. 創建線程池(利用Executors 方式)
1. 創建Callable 或 Runnable任務,提交到線程池
1. 通過返回的 Future#get 獲取返回的結果
對比分析
- 分類
上面實現的4種方式可以分爲2類:
- 繼承Thread類,覆蓋run實現業務邏輯;
- 實現Callable或Runnable接口,通過Thread或線程池來啓動線程
區別
繼承和實現接口的區別
一個是繼承Thread類,可以直接調用實例的 start()方法來啓動線程;另一個是實現接口,需要藉助 Thread#start()來啓動線程,繼承只能單繼承,實現可以多接口,也可以繼承;Runnable和Callable
最根本的區別:
Runnable:無返回結果,使用如下方式:
new Thread(new Runnable() { public void run() {...} }).start()
Callable:有返回結果,使用方式如下:
FutureTask<Object> future = new FutureTask<>(() -> null);
new Thread(future).start();
Object obj=future.get();
Thread啓動和線程池啓動方式
Thread通過Thread#start()來啓動線程
線程池通過ExecutorService#submit()提交線程
參考地址