在Java中,若是要實現多線程,必須依靠一個線程的主體類(就比如主類的概念同樣,表示的是一個線程的主類)。java
可是這個線程的主體類在定義時也須要一些特殊的要求,即類須要繼承Thread類或實現Runnable(Callable)接口來完成定義多線程
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()方法不只要啓動多線程的執行代碼,還要根據不一樣的操做系統進行資源分配
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();
使用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進程都至少啓動一下兩個線程:
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方法只能由一個線程訪問。
概念:兩個線程都在等待彼此先完成,形成了程序的停滯狀態,通常程序的死鎖都是在程序運行時出現的。過多的同步也會形成死鎖。