java基礎:線程及線程池一

前言

多線程編程的根本目的,就是更好的利用cpu資源,採用多線程的方式去同時完成幾件事情而不互相干擾。

1.多線程的一些基本概念

  • **多線程:**指的是這個程序(一個進程)運行時產生了不止一個線程
  • **併發:**通過cpu調度算法,讓用戶看上去同時執行,實際上從cpu操作層面不是真正的同時。併發往往在場景中有公用的資源,那麼針對這個公用的資源往往產生瓶頸,我們會用TPS或者QPS來反應這個系統的處理能力。
  • **線程安全:**經常用來描繪一段代碼。指在併發的情況之下,該代碼經過多線程使用,線程的調度順序不影響任何結果。這個時候使用多線程,我們只需要關注系統的內存,cpu是不是夠用即可。反過來,線程不安全就意味着線程的調度順序會影響最終結果,如不加事務的轉賬代碼。
  • 同步: Java中的同步指的是通過人爲的控制和調度,保證共享資源的多線程訪問成爲線程安全,來保證結果的準確。如上面的代碼簡單加入@synchronized關鍵字。在保證結果準確的同時,提高性能,纔是優秀的程序。線程安全的優先級高於性能。

2. 線程的狀態

線程的狀態
線程在Running的過程中可能會遇到阻塞(Blocked)情況:

  1. 調用join()和sleep()方法,sleep()時間結束或被打斷,join()中斷,IO完成都會回到Runnable狀態,等待JVM的調度。
  2. 調用wait(),使該線程處於等待池(wait blocked pool),直到notify()/notifyAll(),線程被喚醒被放到鎖定池(lock blocked pool ),釋放同步鎖使線程回到可運行狀態(Runnable)
  3. 對Running狀態的線程加同步鎖(Synchronized)使其進入(lock blocked pool ),同步鎖被釋放進入可運行狀態(Runnable)。

此外,在runnable狀態的線程是處於被調度的線程,此時的調度順序是不一定的。Thread類中的yield方法可以讓一個running狀態的線程轉入runnable。

3.多線程編程的兩種方式

3.1 繼承Thread類

繼承Thread類可以一種多線程實現方式,但查看Thread類我們可以發現其也是Runnable接口的一個實例,並且,啓動線程的唯一方法就是通過Thread類的start()實例方法。start()方法是一個native方法,它將啓動一個新線程,並執行run()方法。這種方式實現多線程很簡單,通過自己的類直接extend Thread,並複寫run()方法,就可以啓動新線程並執行自己定義的run()方法。例如:

public class ThreadDemo1 extends Thread{
	@Override
	public void run() {
		//輸出當前線程的名稱
		String name = Thread.currentThread().getName();
		for(int i=0;i<100;i++){
			try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
		System.out.println(name+i);
		}
	}	
}

public class ThreadMethod{	
	public static void main(String[] args) {
		new ThreadDemo01().start();
	}
}

3.2 實現Runnable接口

如果自己的類已經繼承另一個類,就無法直接extends Thread,此時,必須實現一個Runnable接口,如下:

public class ThreadDemo implements Runnable {
	@Override
	public void run() {
		//輸出當前線程的名稱
		String name = Thread.currentThread().getName();
		for(int i=0;i<100;i++){
			System.out.println(name+i);
		}
	}	
}
//爲了啓動線程必須先實例化一個Thread,並傳入子定義的線程實例
public class ThreadMethod{	
	public static void main(String[] args) {
		new Thread(new ThreadDemo()).start();
	}
}

3.3 兩種實現方式區別

  1. 繼承Thread:直接使用Thread類中的方法,代碼簡單 ,由於子類重寫父類的run(),當調用start()時,直接找子類的run()方法 。但由於單繼承的原因受到限制。
  2. 實現Runnable接口:不能直接使用Thread類中的方法,需要先獲取到線程對象後,才能得到Thread的方法,代碼複雜。Thread的構造函數中傳入了Runnable引用,成員變量記住它,start()調用Thread中的run()方法時,判斷成員變量Runnable的引用是否爲空,不爲空則在Thread的run()方法中調用Runnable的run()方法。編譯看Runnable的run(),運行看子類run()方法。 由於可以多實現 因此不受限制。

4.主要的方法

  1. synchronized:給線程加鎖,實現同步
  2. wait():等待, 必須等到notify/notifyAll 方法才能進入runnable狀態
  3. notify/notifyAl
  4. sleep(long l) : 睡眠指定時間
  5. join() :在一個線程中調用other.join(),將等待other執行完後才繼續本線程。
  6. interrupte():
  7. yield():暫停當前正在執行的線程對象,並執行其他線程。
    關於中斷:它並不像stop方法那樣會中斷一個正在運行的線程。線程會不時地檢測中斷標識位,以判斷線程是否應該被中斷(中斷標識值是否爲true)。終端只會影響到wait狀態、sleep狀態和join狀態。被打斷的線程會拋出InterruptedException。

Thread.interrupted()檢查當前線程是否發生中斷,返回boolean
synchronized在獲鎖的過程中是不能被中斷的。

中斷是一個狀態!interrupt()方法只是將這個狀態置爲true而已。所以說正常運行的程序不去檢測狀態,就不會終止,而wait等阻塞方法會去檢查並拋出異常。如果在正常運行的程序中添加while(!Thread.interrupted()) ,則同樣可以在中斷後離開代碼體

5. 線程中獲取異常

多線程不能用try,catch來獲取線程中的異常

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章