Java 多線程

在Java中,若是要實現多線程,必須依靠一個線程的主體類(就比如主類的概念同樣,表示的是一個線程的主類)。java

可是這個線程的主體類在定義時也須要一些特殊的要求,即類須要繼承Thread類或實現Runnable(Callable)接口來完成定義多線程

多線程的實現

繼承Thread類,實現多線程

public class MyThread extends Thread {    //多線程的操做類
    private String name ;
    public MyThread(String name) {
        this.name = name ;
    }
    @Override
    public void run() {    //覆寫run方法做爲線程的主操做類
            for(int x = 0 ; x <200 ;x++) {
                System.out.println(this.name+"-->"+x);
            }
    }

}


main:
     MyThread mt1 = new MyThread("線程A");
     MyThread mt2 = new MyThread("線程B");
     MyThread mt3 = new MyThread("線程C");
     //啓動多線程
     mt1.start();
     mt2.start();
     mt3.start();


爲何多線程啓動不是調用run()而必須調用start()?
  在java的開發裏面有一門技術稱爲Java本地接口(Java Native Interface,JNI)技術。使用Java調用本地操做系統提供的函數。
這個技術不能離開特定的操做系統,若是要執行線程,須要根據操做系統來進行資源的分配。主要是由JVM根據不一樣的操做系統來實現。
即便用Thread類的start()方法不只要啓動多線程的執行代碼,還要根據不一樣的操做系統進行資源分配

實現Runnable接口,實現多線程

public class MyThread2 implements Runnable {  //實現接口 private String name ;
    public MyThread2(String name) {
        this.name = name ;
    }
    @Override
    public void run() {    //覆寫run() for(int x = 0 ; x <200 ;x++) {
            System.out.println(this.name+"-->"+x);
        }
    }
}


main:

/***實現Runnable接口的多線程,,Thread是Runnable接口 的子類(代理),
* 經過Thread類對象包裝Runnable接口對象實例,而後利用Thread 類的start()方法啓動多線程***/
MyThread2 mt01 = new MyThread2("線程1");
MyThread2 mt02 = new MyThread2("線程2");
MyThread2 mt03 = new MyThread2("線程3");

new Thread(mt01).start();
new Thread(mt02).start();
new Thread(mt03).start();

ide

使用Runnable接口能夠有效避免單繼承侷限問題,因此在實際的開發中對於多線程的實現首選Runnable接口函數

兩種實現方式的區別

public class Thread extends Object implements Runnablethis

經過定義能夠發現,Thread類也是Runnable接口的子類,以前利用Runnable接口實現的多線程,實際結構:spa

Runnable接口                     Thread類操作系統

class MyThread  implements Runnable{          class MyThread extends Thread(){線程

@Override                         @Override       3d

  public void run(){//線程主方法                 public void run(){  //線程主方法     代理

  //線程操做方法                        //線程操做方法

  }                              }          

}                             }

 

MyThread mt = new MyThread();              MyThread mt = new MyThread();

new Thread(mt).start();                    mt.start();

利用Callable接口實現多線程

使用Runnable接口能夠避免單繼承的侷限性,可是Runnable接口裏面的run()方法不能返回操做結果。

從jdk1.5開始提供了新的接口:  java.util.concurrent.Callable

@FunctionalInterface
public interface Callable<V>{
      public V call() throws Exception;          
}
public class MyThread implements Callable<String> {

    private int ticket = 0 ;
    @Override
    public String call() throws Exception {
        for(int i = 0 ; i <100; i ++) {
            if(this.ticket > 0) {
                System.out.println("賣出,剩餘"+this.ticket --);
            }
        }
        return "票賣完了!";   //返回結果
    }
}

如何啓動實現Callable接口的多線程?
Thread類沒有定義構造方法能夠直接接收Callble接口對象實例,而且因爲須要接收call()方法返回值的問題。JDK1.5開始,提供了java.util.concurrent.FutureTask<V>
public class FutureTask<V> extends Object implements RunnableFuture<V>

/***實現了callable 接口的多線程,能夠返回結果;
* RunnableFuture接口 實現了Runnable接口和Future接口
* FutureTask 又實現了 RunnableFuture 接口
* ***/
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();

FutureTask<String> task1 = new FutureTask<String>(mt1);
FutureTask<String> task2 = new FutureTask<String>(mt2);
//FutureTask是Runnable接口子類,因此可使用Thread類的構造來接收task對象

new Thread(task1).start();
new Thread(task2).start();

//多線程執行完畢後能夠取得內容
System.out.println("A線程的返回結果"+task1.get());
System.out.println("B線程的返回結果"+task2.get());

線程的操做狀態

 

  1.建立狀態

    在程序中用構造方法建立一個線程對象後,新的線程對象便處於新建狀態。此時已經有相應內存空間和其餘資源,但處於不可運行狀態。

  2.就緒狀態

    新建線程對象後,調用該線程的start()方法就能夠啓動線程。當線程啓動時,線程進入就緒狀態。此時線程將進入線程隊列排隊,等待cpu服務,這代表它已經具有了運行狀態。

  3.運行狀態

    當就緒狀態的線程被調用並得到處理器資源時,線程就進入了運行狀態。此時,自動調用該線程對象的run()方法。

  4.堵塞狀態

    一個正在執行的線程在某些狀況下,如被人爲掛起或須要執行耗時的輸入輸出操做時,將讓出CPU,並暫時停止本身的執行,進入堵塞狀態。在可執行狀態下,若是調用sleep()、suspend()、wait()等方法,線程都將進入堵塞狀態。堵塞時,線程不能進入排隊隊列,只有當引發堵塞的緣由被消除後,線程才能夠轉入就緒狀態。

  5.終止狀態

  線程調用stop()方法時候run()方法執行結束後,就處於終止狀態。處於終止狀態的線程不具備繼續運行的能力

多線程經常使用操做方法

線程的命名與取得

public Thread(Runnable target, String name)  構造方法  實例化線程對象,接受Runnable接口子類對象,同時設置線程名字

public final void setName(String name)  普通方法  設置線程名字

public final String getName()  普通方法  取得線程名字

 

進程在哪裏?

  當用戶使用Java命令執行一個類時就表示啓動了一個JVM的進程,而主方法只是進程上的一個線程而已,當一個類執行完畢後,此進程會自動消失。

  並且每一個JVM進程都至少啓動一下兩個線程:

    • main線程:程序的主要執行,以及啓動子線程
    • gc線程:負責垃圾的收集。          

線程的休眠

 public static void sleep(long millis) throws InterruptedException ,設置的休眠單位時間是毫秒(ms)

線程優先級

  在Java線程操做中,全部的線程在運行前都會保持就緒狀態,此時哪一個線程的優先級高,就可能先被執行。

public static final int MAX_PRIORITY  常量  最高優先級,數值爲10

public static final int NORM_PRIORITY  常量  中等優先級,數值爲5

public staic final int MIN_PRIORITY  常量  最低優先級,數值爲1

public final void setPriority(int newPriority)  普通  設置線程優先級

public final int getPriority()  普通  取得線程優先級

線程的同步與死鎖

當多個線程操做同一資源時,就有可能出現不一樣步的狀況:尚未等到前一個線程的執行結果就進行了下一個線程的操做,從而出現問題。

例如:

  多個線程進行賣票操做,當票數爲1的時候,前一個線程獲取到數量爲1的票數,還沒等它的執行完成,後一個線程也獲取到了數量爲1的票數開始執行。最終出現問題

同步操做

一個代碼塊中的多個操做在同一個時間段內只能有一個線程進行,其餘線程要等待此線程完成後才能夠繼續執行。

實現同步操做可使用synchronized關鍵字。synchronized關鍵字能夠經過如下兩種方式進行使用:

  1.同步代碼塊,利用synchronized包裝的代碼塊,可是須要指定同步對象,通常設置爲this;

  2.同步方法,利用synchronized定義的方法

 

同步代碼塊:

public MyThread implements Runnable{
 
  private int ticket = 5 ;
  @Override
  public void run(){
     for(int x =0;x<20;x++){
      synchronized(this){
      if(this.ticket > 0){
        //賣票操做
      }
     }     } } }

同步方法:
public MyThread implements Runnable{
 
  private int ticket = 5 ; @Override public void run(){ for(int x =0;x<20;x++){       this.sale()  //調用synchronized方法
   } }

public synchronized void sale(){
    //賣票操做。。。
}

}
 

 

 

 

abstract的method是否能夠同時是static,是否能夠同時是native,是否能夠同時是synchronized?

  method,static,natice,synchronized都不能和「abstract」同時聲明方法

 

當一個線程進入一個對象的synchronized方法後,其餘線程是否能夠訪問此對象的其餘方法?

  不能訪問,一個對象操做一個synchronized方法只能由一個線程訪問。

死鎖

概念:兩個線程都在等待彼此先完成,形成了程序的停滯狀態,通常程序的死鎖都是在程序運行時出現的。過多的同步也會形成死鎖。

生產者和消費者問題

線程的生命週期

相關文章
相關標籤/搜索