程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 0036 Java學習筆記-多線程-創立線程的三種方式

0036 Java學習筆記-多線程-創立線程的三種方式

編輯:關於JAVA

0036 Java學習筆記-多線程-創立線程的三種方式。本站提示廣大學習愛好者:(0036 Java學習筆記-多線程-創立線程的三種方式)文章只能為提供參考,不一定能成為您想要的結果。以下是0036 Java學習筆記-多線程-創立線程的三種方式正文


創立線程

  • 創立線程的三種方式:
    • 承繼java.lang.Thread
    • 完成java.lang.Runnable接口
    • 完成java.util.concurrent.Callable接口
  • 一切的線程對象都是Thead及其子類的實例
  • 每個線程完成一定的義務,其實就是一段順序執行的代碼

承繼java.lang.Thread創立線程

package testpack;

public class Test1  { 
    public static void main(String[] args){ 
        System.out.println("如今是主線程: "+Thread.currentThread());  //用靜態辦法獲取以後正在執行的線程
        System.out.println("上面新建兩個線程");
        new A(100).start();
        new A(100).start();                                       //這裡開啟兩個線程,但不是完成同一件義務
    }
}
class A extends Thread{                                           //承繼Thread類
    private int tickets;
    A (int tick){
        tickets=tick;
    }
    public void run(){
        for (;tickets>0;tickets--) {
            System.out.println("以後線程:"+getName()+" 賣出第 "+tickets+" 張票。");  //用this取得以後線程
            if (tickets==1){
                System.out.println("票已賣完,以後線程是: "+getName());
            }
        }
    }
}
  • 承繼Thread類,開啟線程的步驟:
    • 定義一個類,承繼Thread,重寫run()辦法,run()辦法就是線程要完成的義務
    • 創立該類的實例,並調用start()辦法啟動線程
  • 承繼Thread類開啟的線程,各自完成各自的義務

完成java.lang.Runnable接口創立線程

package testpack;

public class Test1  { 
    public static void main(String[] args){ 
        System.out.println("如今是主線程: "+Thread.currentThread()); 
        System.out.println("上面新建兩個線程");
        A a=new A(100);                                   //創立一個義務的對象
        new Thread(a,"線程A").start();                    //以同一個義務對象為target,開啟兩個線程
        new Thread(a,"線程B").start();                    //兩個線程完成同一項義務,但是二者協作溝通不好
    }
}
class A implements Runnable{                               //完成Runnable接口
    private int tickets;
    A (int tick){
        tickets=tick;
    }
    public void run(){                                     //異樣重寫run()辦法,就是線程要完成的義務
        for (;tickets>0;tickets--) {
            System.out.println("以後線程:"+Thread.currentThread()+" 賣出第 "+tickets+" 張票。");
            if (tickets==1){
                System.out.println("票已賣完,以後線程是: "+Thread.currentThread());
            }
        }
    }
}
  • 完成Runnable接口開啟線程的步驟:
    • 定義一個類,完成Runnbale接口,重寫run()辦法
    • 創立該類的對象,即義務對象
    • 以義務對象為target,創立線程,調用start()辦法
  • 經過Runnable接口創立的多個線程,可以寫作完成同一件義務,只是協作方面有待改良
  • 另外,自1.8開端,Runnable用了\@FunctionalInterface修飾,可以用Lambda表達式創立Runnable對象了

運用Callable和Future創立線程

package testpack;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class Test1  { 
    public static void main(String[] args){ 
        Callable a=new A(100);                  //Callble對象不能直接用於Thread的target    
        FutureTask task=new FutureTask(a);      //FutureTask完成了Future接口和Runnable接口,可以用作target
        new Thread(task,"線程A").start();
        new Thread(task,"線程B").start();       //奇異的是,該線程未運轉,學到前面再看,估量用法不對。
        try{
            System.out.println(task.get());     //get()辦法獲取線程義務的前往值,無參數則阻塞到線程執行完成
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }
}
class A implements Callable<String>{           //完成Callable接口,留意泛型
    private int tickets;
    A (int tick){
        tickets=tick;
    }
    public String call(){                      //重寫call()辦法,相當於Runnable的run()辦法,但有前往值
        for (;tickets>0;tickets--) {
            System.out.println("以後線程:"+Thread.currentThread()+" 賣出第 "+tickets+" 張票。");
        }
        if (tickets==0) {
            return "一切票都已賣出!";
        }else{
            return "票未全部賣出,有異常";
        }
    } 
}
  • Callable相似於Runnable,只是前者有前往值、可以拋出異常,後者則不可以;前者不能直接用於target,得用先用FutureTask包裝,後者可以直接做target;
  • FutureTask完成了Future和Runnable接口,Future接口包括的一些辦法:
    • V get():獲取call辦法的前往值,該辦法會阻塞,不斷等到線程執行完畢前往值
    • V get(long timeout,TimeUnit unit):獲取call()的前往值,但只等候timeout和unit的時間,到時未有前往值,則拋出TimeoutException
    • boolean isDone():線程義務能否執行完畢
    • boolean isCanclled():在義務完成前取消,則前往true
    • boolealn cancel(boolean mayInterruptIfRunning):取消Callable義務
  • Callable有泛型;也是個函數式接口,可以用Lambda表達式直接創立Callable對象
  • 用Callable創立線程義務的步驟:
    • 定義一個類,完成Callable接口,重寫call()辦法。Java8開端,可以用Lambda表達式直接創立對象
    • 創立該類的對象,再用FutureTask包裝
    • 以FutureTask為target創立線程,並start
    • 調用FutrueTask的get()辦法獲取前往值

Thread、Runnable、Callable的比擬

  • Thread:
    • 不能承繼其他類;
    • 用this獲取以後線程
    • 多個線程不能執行同一個target義務
  • Runnable:
    • 可以承繼其他類;
    • 用Thread.currentThread()獲取以後線程
    • 多個線程可以執行同一個target義務
  • Callable:
    • 可以承繼其他類;
    • 用Thread.currentThread()獲取以後線程
    • 多個線程可以執行同一個target義務

線程的生命周期

  • 新建(New):
    • 創立線程對象後,就處於“新建”形態,此時跟一個普通的對象無異
    • 只能對新建形態的線程對象,調用start()辦法
  • 就緒(Runnable):
    • 調用start()辦法後,處於就緒形態,虛擬機為其創立辦法調用棧和順序計數器
    • 何時開端運轉,取決於JVM的調度
    • 就緒形態能夠從“新建”、“阻塞”、“運轉”三種形態變化而來
  • 運轉(Running):
    • 線程取得了在CPU上運轉的權益,開端運轉
    • 何時中綴執行,也取決於調度,一個線程不能夠不斷占著cpu
    • 一個線程,可以調用sleep()或許yield()辦法,來自動保持執行權益
  • 阻塞(Blocked):
    • 調用sleep()辦法
    • 調用了一個阻塞式的IO辦法,該辦法前往之前,被阻塞
    • 試圖取得一個同步監視器,但該監視器被其他線程所持有
    • 等候其他線程的notify()告訴
    • 調用suspend()辦法將該線程掛起。盡量防止該種辦法,能夠招致死鎖
  • 死亡(dead):
    • 線程義務執行完成
    • 線程拋出未捕捉的異常或錯誤
    • 調用線程的stop()辦法
    • 主線程運轉完畢,不影響子線程的持續運轉
    • boolean isAlive()辦法前往線程能否已死亡:處於新建和死亡兩種形態前往false,其他三種形態前往true
  • 見上面的線程形態轉換圖:來源於《瘋狂Java講義 第三版》

線程狀態轉換圖

start()與run()辦法

  • 永遠不要調用線程對象的run()辦法。直接調用run()辦法的話,那麼就只是一個普通辦法,而不是線程義務
  • 調用了run()辦法之後,會改動線程對象的形態,不再是新建形態,因此也就不能調用start()辦法
  • 只能對處於新建形態的線程對象調用start()辦法,且只能調用一次,否則拋出IllegalThreadStateException異常
    
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved