線程實現的三種方式


  Java中使用Thread代表線程類,所有的線程類都必須是Thread類或者其子類的實例。每個線程的作用就是完成一定的任務,即執行一段程序流。Java中使用線程執行體來表示這段程序流。

 

Java中線程的實現方式有如下三種:

1.繼承Thread類

public class Thread extends Object implements Runnable

定義Thread類的子類,並重寫Thread類的run()方法,創建子類對象(即線程對象),調用線程對象的start()方法來啟動該線程。eg:

 

  1.  
    public class ThreadDemo extends Thread {
  2.  
     
  3.  
    private int i;
  4.  
    public void run(){
  5.  
    for(; i<100 ;i++){
  6.  
    System.out.println(getName() + " "+ i);
  7.  
    }
  8.  
    }
  9.  
    public static void main(String[] args) {
  10.  
    for(int i = 0 ;i<100; i++){
  11.  
    //currentThread是Thread類的靜態方法,該方法返回當前正在執行的線程對象
  12.  
    //getName()返回當前線程對象的名字
  13.  
    System.out.println(Thread.currentThread().getName()+ " "+i);
  14.  
    if(i==20){
  15.  
    //啟動兩個線程,但是實際上有三個線程,即main主線程
  16.  
    //用戶啟動的多個線程的名字依次為Thread-0、Thread-1、、、、
  17.  
    new ThreadDemo().start();
  18.  
    new ThreadDemo().start();
  19.  
    }
  20.  
    }
  21.  
     
  22.  
    }
  23.  
     
  24.  
    }

 

注意:該例中第一次出現的變量i是實例變量,而每次創建線程對象時候,Thread-0和Thread-1兩個線程對象不能共享實例變量i。即使用繼承Thread方法創建線程對象時,多個線程之間無法共享線程類的實例變量。

 

2.實現Runnable接口

public interface Runnable

定義Runnable接口的實現類,並重寫該接口的run()方法,該run()方法同樣是該線程的執行體。創建該Runnable實現類的實例,並將此實例作為Thread的target(即構造函數中的參數)來創建Thread對象(該Thread對象才是真正的線程對象,只是該Thread對象負責執行其target的run()方法)。最后調用線程對象的start()方法來啟動該線程。eg:

 

  1.  
    public class ThreadDemo implements Runnable {
  2.  
     
  3.  
    private int i;
  4.  
    <strong> public void run</strong>(){
  5.  
    for(; i<100 ;i++){
  6.  
    //當線程類實現Runnable接口時,只能通過Thread.currentThread()方法獲得當前線程
  7.  
    System.out.println(Thread.currentThread().getName() + " "+ i);
  8.  
    }
  9.  
    }
  10.  
    public static void main(String[] args) {
  11.  
    for(int i = 0 ;i<100; i++){
  12.  
    System.out.println(Thread.currentThread().getName()+ " "+i);
  13.  
    if(i==20){
  14.  
    ThreadDemo td = new ThreadDemo();
  15.  
    //創建兩個Thread對象,並且均把Runnable接口實例對象作為target
  16.  
    new Thread(td).start();
  17.  
    new Thread(td).start();
  18.  
    }
  19.  
    }
  20.  
     
  21.  
    }
  22.  
     
  23.  
    }


從運行的結果可以看到實例變量i的輸出是連續的。也就是使用Runnable接口創建的多個線程是可以共享線程類的實例變量,這是因為多個線程可以共享同一個target,所以多個線程可以共享同一個線程類(target類)的實例屬性。

 

3.使用Callable和Future

public interface Future<V> 

public interface Callable<V>

public interfaceRunnableFuture<V> extends Runnable, Future<V>

public class FutureTask<V>extends Object implementsRunnableFuture<V>

      Java5之后,提供了Callable接口,看起來像是Runnable接口的增強版:該接口提供call()方法來作為線程執行體。與run()相比,call()方法更強大,該方法可以有返回值,並且可以聲明拋出異常。

     但是Callable不是Runnable接口的子類,不能作為Thread的target。而且call()方法返回的值如何調用呢?

     Java5給出了Future接口,來獲得call()方法的返回值。並提供了FutureTask實現類,該類實現了Runnale接口和Future接口---可以作為Thread類的target。

     創建並啟動有返回值的線程的步驟如下:

     創建Callable接口的實現類,並實現call()方法,該方法有返回值;創建Callable實現類的實例,使用FutureTask來包裝Callable對象,並且也封裝了call()方法的返回值;使用FutureTask作為Thread類的target創建並啟動線程;調用FutureTask對象的get()方法返回子線程執行結束后的返回值。

 

  1.  
    import java.util.concurrent.Callable;
  2.  
    import java.util.concurrent.FutureTask;
  3.  
     
  4.  
    public class CallableDemo implements Callable<Integer>{
  5.  
    //實現call()方法,作為線程執行體
  6.  
    public Integer call(){
  7.  
    int i = 5;
  8.  
    for( ; i<100 ; i++){
  9.  
    System.out.println(Thread.currentThread().getName() + "的循環變量i的值:" +i);
  10.  
    }
  11.  
    //call()方法可以有返回值
  12.  
    return i;
  13.  
    }
  14.  
    public static void main(String[] args) {
  15.  
    //創建Callable對象
  16.  
    CallableDemo cd = new CallableDemo();
  17.  
    //使用FutureTask來包裝Callable對象
  18.  
    FutureTask<Integer> task = new FutureTask<Integer>(cd);
  19.  
    for(int i=0 ; i<100 ; i++){
  20.  
    System.out.println(Thread.currentThread().getName() + "的循環變量i的值:" +i);
  21.  
    if(i==20){
  22.  
    //實質還是以Callable對象來創建並啟動線程
  23.  
    new Thread(task,"有返回值的線程").start();
  24.  
    }
  25.  
    }
  26.  
    try{
  27.  
    System.out.println( "子線程的返回值" + task.get());
  28.  
     
  29.  
    } catch(Exception e){
  30.  
    e.printStackTrace();
  31.  
    }
  32.  
     
  33.  
    }
  34.  
     
  35.  
    }


 FutureTask是Future接口的實現類。Future接口提供了一些方法來控制他關聯的Callable任務。

 

boolean cancel(boolean mayInterruptIfRunning)
Attempts to cancel execution of this task. //視圖取消該Future關聯的Callable任務
V get()
Waits if necessary for the computation to complete, and then retrieves its result.
//返回call()方法的返回值,調用該方法將導致程序阻塞,必須等到子線程結束后才會有返回值
V get(long timeout, TimeUnit unit)
Waits if necessary for at most the given time for the computation to complete, and then retrieves its result, if available.
//設置阻塞時間
boolean isCancelled()
Returns true if this task was cancelled before it completed normally.
boolean isDone()
Returns true if this task completed.

Callable有泛型限制,Callable接口里的泛型形參類型與Call()方法返回值類型相同。

 

4.創建線程的三種方式對比

實現Runnable和Callable接口的方法基本相同,只不過Callable接口定義的方法可以有返回值,而且可以聲明拋出異常而已。

因此采用實現Runnable和Callable接口方式創建多線程——

優勢:

1.線程類只是實現了接口類,還可以繼承其他類

2.在這種方式下,多個線程可以共享同一個target對象,所以非常適合多個相同線程類處理同一份資源的情況。

劣勢:

編程稍稍復雜,如果要訪問當前線程必須使用Thread.currentThread()方法

而繼承Thread方式則與之相反,因為已經繼承了Thread類,不能再繼承其他類。編寫簡單,如果要訪問當前線程,直接使用this即可獲得當前線程。

故一般建議采用實現Runnable、Callable接口的方式實現多線程。

--------------------- 本文來自 亮仔亮仔我愛你喲 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/fffllllll/article/details/52269568?utm_source=copy 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM