Java 多線程編程

1、線程的生命週期java

線程是一個動態執行的過程,它也有一個從產生到死亡的過程。多線程

下圖顯示了一個線程完整的生命週期this

  • 新建狀態:

    使用 new 關鍵字和 Thread 類或其子類創建一個線程對象後,該線程對象就處於新建狀態。它保持這個狀態直到程序start() 這個線程。spa

  • 就緒狀態:

    當線程對象調用了start()方法以後,該線程就進入就緒狀態。就緒狀態的線程處於就緒隊列中,要等待JVM裏線程調度器的調度。線程

  • 運行狀態:

    若是就緒狀態的線程獲取 CPU 資源,就能夠執行 run(),此時線程便處於運行狀態。處於運行狀態的線程最爲複雜,它能夠變爲阻塞狀態、就緒狀態和死亡狀態。3d

  • 阻塞狀態:

    若是一個線程執行了sleep(睡眠)、suspend(掛起)等方法,失去所佔用資源以後,該線程就從運行狀態進入阻塞狀態。在睡眠時間已到或得到設備資源後能夠從新進入就緒狀態。能夠分爲三種:code

    • 等待阻塞:運行狀態中的線程執行 wait() 方法,使線程進入到等待阻塞狀態。對象

    • 同步阻塞:線程在獲取 synchronized 同步鎖失敗(由於同步鎖被其餘線程佔用)。blog

    • 其餘阻塞:經過調用線程的 sleep() 或 join() 發出了 I/O 請求時,線程就會進入到阻塞狀態。當sleep() 狀態超時,join() 等待線程終止或超時,或者 I/O 處理完畢,線程從新轉入就緒狀態。繼承

  • 死亡狀態:

    一個運行狀態的線程完成任務或者其餘終止條件發生時,該線程就切換到終止狀態

2、線程的優先級

一、調整線程優先級:Java線程有優先級,優先級高的線程會得到較多的運行機會
Java線程的優先級用整數表示,取值範圍是1~10,Thread類有如下三個靜態常量:
static int MAX_PRIORITY
          線程能夠具備的最高優先級,取值爲10。
static int MIN_PRIORITY
          線程能夠具備的最低優先級,取值爲1。
static int NORM_PRIORITY
          分配給線程的默認優先級,取值爲5。
Thread類的setPriority()和getPriority()方法分別用來設置和獲取線程的優先級

具備較高優先級的線程對程序更重要,而且應該在低優先級的線程以前分配處理器資源。可是,線程優先級不能保證線程執行的順序,並且很是依賴於平臺

3、建立一個線程

Java 提供了三種建立線程的方法:

  • 經過實現 Runnable 接口
  • 經過繼承 Thread 類自己
  • 經過 Callable 和 Future 建立線程

1)實現 Runnable 接口來建立線程

建立一個線程,最簡單的方法是建立一個實現 Runnable 接口的類,爲了實現 Runnable,一個類只須要執行一個方法調用 run()

1、建立一個對象類

package test_synthronized;
public class Foo {
   int x=100;

public int getX() {
    return x;
}
public  int fix(int y) {
    synchronized(this){
    x=x-y;
    System.out.println("線程"+Thread.currentThread().getName() + "運行結束,減小「" + y
            + "」,當前值爲:" + x);}
    return x;
}
}
2、建立一個線程類

package test_synthronized;

/**
 * 線程的同步與鎖
 */
public class Thread_synchronized_01 implements Runnable{
    private  Foo foo=new Foo();
   
   public static void main(String[] args) {
       Thread_synchronized_01 syn=new Thread_synchronized_01();
       Thread t1=new Thread(syn,"t1"); 
       Thread t2=new Thread(syn,"t2"); 
       t1.start();
       t2.start();
   }

    /* (non-Javadoc)
     * @see java.lang.Runnable#run()
     */
    public void run() {
         for (int i = 0; i < 3; i++) {
                this.fix(30);
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
     /* 
      * t1 : 當前foo對象的x值= 40
        t2 : 當前foo對象的x值= 40
        t1 : 當前foo對象的x值= -20
        t2 : 當前foo對象的x值= -50
        t1 : 當前foo對象的x值= -80
        t2 : 當前foo對象的x值= -80
      *  從結果發現,這樣的輸出值明顯是不合理的。緣由是兩個線程不加控制的訪問Foo對象並修改其數據所致。
                若是要保持結果的合理性,只須要達到一個目的,就是將對Foo的訪問加以限制,每次只能有一個線程在訪問。這樣就能保證Foo對象中數據的合理性了。
                在具體的Java代碼中須要完成一下兩個操做:
                把競爭訪問的資源類Foo變量x標識爲private;
                同步哪些修改變量的代碼,使用synchronized關鍵字同步方法或代碼。
      */
                System.out.println(Thread.currentThread().getName() + " : 當前foo對象的x值= " + foo.getX());
            }
    }
    public int fix(int y) {
        return foo.fix(y);
    }
}
3、結果
線程t1運行結束,減小「30」,當前值爲:70
t1 : 當前foo對象的x值= 70
線程t1運行結束,減小「30」,當前值爲:40
t1 : 當前foo對象的x值= 40
線程t1運行結束,減小「30」,當前值爲:10
t1 : 當前foo對象的x值= 10
線程t2運行結束,減小「30」,當前值爲:-20
t2 : 當前foo對象的x值= -20
線程t2運行結束,減小「30」,當前值爲:-50
t2 : 當前foo對象的x值= -50
線程t2運行結束,減小「30」,當前值爲:-80
t2 : 當前foo對象的x值= -80

2) 繼承Thread來建立線程

第二種方法是建立一個新的類,該類繼承 Thread 類,而後建立一個該類的實例。

繼承類必須重寫 run() 方法,該方法是新線程的入口點。它也必須調用 start() 方法才能執行。

該方法儘管被列爲一種多線程實現方式,可是本質上也是實現了 Runnable 接口的一個實例

package test_synthronized;
public class Thread_synchronized_02 {

class MyThread extends Thread{ private Foo foo; /**當前值*/ private int y = 0; MyThread(String name, Foo foo, int y) { super(name); this.foo = foo; this.y = y; } public void run() { foo.fix(y); } } public static void main(String[] args) { Thread_synchronized_02 run = new Thread_synchronized_02(); Foo foo=new Foo(); MyThread t1 = run.new MyThread("線程A", foo, 10); MyThread t2 = run.new MyThread("線程B", foo, 2); MyThread t3 = run.new MyThread("線程C", foo, 3); MyThread t4 = run.new MyThread("線程D", foo, 5); t1.start(); t2.start(); t3.start(); t4.start(); } } 結果 線程線程A運行結束,減小「10」,當前值爲:90 線程線程C運行結束,減小「3」,當前值爲:87 線程線程B運行結束,減小「2」,當前值爲:85 線程線程D運行結束,減小「5」,當前值爲:80

3) 經過 Callable 和 Future 建立線程

  • 1. 建立 Callable 接口的實現類,並實現 call() 方法,該 call() 方法將做爲線程執行體,而且有返回值。

  • 2. 建立 Callable 實現類的實例,使用 FutureTask 類來包裝 Callable 對象,該 FutureTask 對象封裝了該 Callable 對象的 call() 方法的返回值。

  • 3. 使用 FutureTask 對象做爲 Thread 對象的 target 建立並啓動新線程。

  • 4. 調用 FutureTask 對象的 get() 方法來得到子線程執行結束後的返回值

package test_synthronized;

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

public class CallableThread implements Callable<Integer> {

    public Integer call() throws Exception {
        int i = 0;  
        for(;i<8;i++)  
        {  
            System.out.println(Thread.currentThread().getName()+" "+i);  
        }  
        return i;
    }
    public static void main(String[] args) {
        CallableThread callableThread=new CallableThread();
        FutureTask<Integer> ft=new FutureTask<Integer>(callableThread);
         for(int i = 0;i < 8;i++)  
         {  
             System.out.println(Thread.currentThread().getName()+" 的循環變量i的值"+i);  
             if(i==2)  
             {  
                 new Thread(ft,"有返回值的線程").start();  
             }  
         }  
         try  
         {  
             System.out.println("子線程的返回值:"+ft.get());  
         } catch (InterruptedException e)  
         {  
             e.printStackTrace();  
         } catch (ExecutionException e)  
         {  
             e.printStackTrace();  
         } 
    }
}
結果
main 的循環變量i的值0
main 的循環變量i的值1
main 的循環變量i的值2
main 的循環變量i的值3
有返回值的線程 0
main 的循環變量i的值4
有返回值的線程 1
main 的循環變量i的值5
有返回值的線程 2
main 的循環變量i的值6
有返回值的線程 3
main 的循環變量i的值7
有返回值的線程 4
有返回值的線程 5
有返回值的線程 6
有返回值的線程 7
子線程的返回值:8

4、線程的三種方式的對比

  • 1. 採用實現 Runnable、Callable 接口的方式創見多線程時,線程類只是實現了 Runnable 接口或 Callable 接口,還能夠繼承其餘類。

  • 2. 使用繼承 Thread 類的方式建立多線程時,編寫簡單,若是須要訪問當前線程,則無需使用 Thread.currentThread() 方法,直接使用 this 便可得到當前線程。

相關文章
相關標籤/搜索