併發編程(二)—線程的創建方式和對比

併發編程中,線程創建的是很常見的,創建線程的方式也分幾種,一般創建線程有如下四種方式:

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()提交線程

    參考地址


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章