JAVA中線程的相關小結

·什麼是線程

線程:進程中負責程序執行的執行單元。一個進程中至少有一個線程。java

多線程:一個進程中包含有多個線程,但CPU在同一時間只容許一個線程的進行。因此有多個線程的運行是根據CPU切換完成,如何切換由CPU決定,所以多線程運行具備不肯定性。bash

·線程的建立方法

1.繼承Thread類

即建立一個thread類,或者建立一個thread子類的對象多線程

1>Thread類的介紹ui

  • Thread類是一個線程類,位於java.lang包下this

  • Thread的主要構造方法:spa

    1.1 thread() 建立一個線程對象操作系統

    1.2 thread(String name) 建立一個具備指定名稱的線程對象線程

    1.3 thread(Runnable target) 建立一個基於runnable接口實現類的線程對象code

    1.4 thread(Runnable target,string name) 建立一個基於Runnable接口實現類,而且具備指定名稱的線程對象cdn

  • 經常使用方法: 1.1 public void run() 線程相關的代碼寫在該方法中,通常須要重寫

    1.2 public void start() 啓動線程

    1.3 public static void sleep(long m) 線程休眠m毫秒

    1.4 public final void join() 優先執行調用join方法的線程

2>具體的代碼實現

package _Thread;

class mythread extends Thread{
//重寫Run方法
	public void run() {
		System.out.println(getName()+"線程");
	}
}

public class _threadTest {
	public static void main(String[] args) {
		mythread sr=new mythread();
		sr.start;//啓動線程
	}
}
複製代碼

實現結果

而後爲了將CPU時間片的輪轉體現出來,將加入2個循環以下:

package _Thread;

class mythread extends Thread{
	public void run() {
		for(int i=1;i<=10;i++)
		System.out.println(getName()+"線程"+i+"次");
	}
}

public class _threadTest {
	public static void main(String[] args) {
		mythread sr=new mythread();
		sr.start();
		for(int i=1;i<=10;i++)
			System.out.println("主線程"+i+"次");
	}
}

複製代碼

兩次不一樣的運行結果:

這說明線程的運行是有cpu隨機切換完成的,具備不肯定性。

tips:

線程只能啓動一次

主方法也是一個線程

2.實現Runnable接口

1>Runnable類的介紹

因爲JAVA是不支持多繼承的,因此若是一個類已經有一個父類,那麼咱們就沒法經過繼承Threadl類來建立線程,此時咱們就能夠經過實現Runnable接口來完成。Runnable是java中實現線程的接口,它有且只有一個方法Run(),而且任何實現線程功能的類必須實現該接口。

2>具體的代碼實現

package _Thread;

class mythread implements Runnable{
	public void run() {
		System.out.println(Thread.currentThread().getName()+"線程");
		/*
		因爲這裏沒有繼承Thread類中的方法,
		因此這裏調用Thread中的currentThread()的getName()來獲取當前線程的名字
		*/
	}
}

public class _threadTest {
	public static void main(String[] args) {
		mythread sr=new mythread();
		Thread pr=new Thread(sr);//經過調用Thread類中的含參構造方法來建立一個線程對象
		pr.start();
	}
}

複製代碼

tips:一個實例能夠被多個線程所共享,通常用於多個線程處理同一個資源的狀況。

·線程的狀態和生命週期

1.線程的幾種狀態

1.1新建(NEW)狀態
複製代碼

當程序使用new關鍵字建立了一個線程以後,該線程就處於新建狀態.

1.2可運行(Runnable)狀態
複製代碼

該狀態的線程位於可運行線程池中,等待被線程調度選中,獲取cpu 的使用權 。

·關於直接調用run()方法

若是直接調用線程對象的run方法,也就是至關於啓動線程,並直接執行run方法,並且再run方法結束以前沒法運行其餘線程。

1.3正在運行(Runnming)狀態
複製代碼

當CPU開始調度處於可運行狀態的線程時,此時線程得到了CPU的時間片才得以真正開始執行run()方法的線程執行體,則該線程處於可運行狀態。

1.4阻塞(Blocked)狀態
複製代碼

處於運行狀態的線程在某些狀況下,讓出CPU並暫時中止該線程的運行,也就是進入了阻塞狀態。

1.5終止(Dead)狀態
複製代碼

線程的run()或call()方法執行完成,線程會正常結束;

線程拋出了一個未捕獲的Exception或Errorl異常,線程會意外終止;

或者直接調用該線程stop()方法來結束該線程,但可能會致使線程的死鎖;

2.線程的生命週期

線程的生命週期其實是指線程的幾種狀態的之間的切換。

生命週期圖以下:

這些狀態的切換每每是經過調用Thread類中的方法來實現的,下面介紹幾種經常使用的方法:

2.1  public static native void sleep(long millis)
複製代碼
  • 做用:使當前正在運行的線程休眠millis毫秒
  • 因爲該線程是從運行狀態到阻塞狀態,再到可運行狀態,並非直接到可運行狀態。因此中間會產生必定的時間偏差,使其略長於所設參數。
  • 該方法是在Thread類中的一個靜態方法。
2.2  object.wait()
複製代碼
  • 做用:暫停該線程,釋放對象鎖
  • 該方法是在object類中的方法
  • 在調用前須要先擁有某對象的鎖,因此通常在 synchronized 同步塊中使用

·對象鎖簡單的理解就是在代碼中的方法上加了synchronized的鎖,或者synchronized(this)的代碼段 於之對應的還有類鎖,指在代碼中的方法上加了static和synchronized的鎖(在下面還會詳細涉及到)。

2.3 Thread.yield()
複製代碼
  • 做用:表示暫停當前線程,讓出 CPU給優先級與當前線程相同,或者優先級比當前線程更高的就緒狀態的線程。
  • 須要注意的是這和 sleep() 方法不一樣的是,它不會進入到阻塞狀態,而是直接進入到可運行狀態。
2.4 Thread.join()
複製代碼
  • 會優先將該線程運行完畢,再去調用其餘線程,也就是等待該線程的終止

  • 其重載方法還有

    public final void join(long millis)等待該線程終止的時間最長爲 millis 毫秒

    public final void join(long millis, int nanos)等待該線程終止的時間最長爲 millis 毫秒 + nanos 納秒

·線程的優先級

Java 把線程優先級分紅10個級別(依次爲1到10,數字越大線程的優先級越高),線程被建立時若是沒有明確聲明則使用默認優先級(5)。雖然優先級越高越先被執行,但咱們不能用線程優先級來規定線程的執行順序。由於線程的執行順序還受不少其餘因素影響,例如操做系統和JVM的調度等等,因此咱們只能說線程優先級越大,越先被執行的可能性越高。這裏也體現了CPU調度的隨機性。

1.三個優先級的常量

最小優先級(1)=Thread.MIN_PRIORITY

默認優先級(5)=Thread.NORM_PRIORITY

最大優先級(10)=Thread.MAX_PRIORITY

2.優先級的相關方法

2.1 public int getPriority()
複製代碼
  • 做用:取得線程的優先級
2.2 public void setPriority(int newPriority)
複製代碼
  • 做用:設置線程的優先級

·線程的同步

因爲線程被執行的時間、佔用cpu的時間都是不肯定的,因此在有多個線程要被執行的狀況下,會產生許多問題。例,在有兩個線程(線程1和線程2)對同一個類的屬性進行操做時,若是線程1對該屬性進行「加」的操做前,線程2忽然搶佔了CPU來對該屬性進行了「減」的操做,對該屬性進行修改,那麼就可能出現相似於1+1=1的結果。

爲了不上述結果的出現,這裏須要調用一個synchronized關鍵字(同步),來對該共享對象進行上鎖,即讓該共享對象在同一個時刻,只能被一個線程訪問。

synchronized關鍵字的使用

通常加在線程前,代表該線程不容許被打斷, 能夠加在靜態方法/方法/語句塊前。 例:

public synchronized void  方法名(){}
複製代碼
public static synchronized void  方法名(){}
複製代碼
synchronized(對象名){}
複製代碼

線程間的通訊

咱們先引進三個方法

wait()中斷方法的執行,使線程等待
複製代碼
notify()隨機喚醒等待的某一個線程
複製代碼
notifyall()喚醒全部處於等待的線程
複製代碼

對多線程的處理,下面以對2個線程的處理爲例

通常是將公共對象看成一個容器queue,並在該容器內設置一個Boolean類型的值,在將線程1和2設置爲synchronized類型後,在線程1中添加判斷語句,若是該Boolean型值爲ture(也能夠是flase)才能執行該線程的操做語句,不然進入等待(在這裏調用wait()使其暫停運行,記得要在線程最後改變Boolean類型值的值),對線程2的操做也相似,這樣就能解決對線程調度順序問題。

但這樣問題並非徹底被解決,若是線程1和2都進入休眠狀態,那麼鎖死,即兩個線程都不進行。因此咱們還須要在線程1和線程2的最後加上喚醒醒另外一個線程的語句(這裏調用notifyall(),之因此不調用notify()是由於它是隨機喚醒某一個線程,若是在程序中還有線程三、線程4···,那咱們想喚醒的線程有可能不被喚醒)

相關文章
相關標籤/搜索