四種創建線程的方式

最近一直在刷面試題,刷到“如何創建進程”,所以記錄下來

  進程一直是面試的熱點問題,創建進程的方式有四種,分別是繼承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框架知識。鏈接如下

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