java多線程

java多線程

參考博客:

  1. Java多線程詳解

一. 概述

  • 首先,我們需要分清楚一些概念,什麼是進程(進城),什麼是線程(獻城).
    在任務管理器中我們可以看到進程在執行.還有一些進程的信息,例如CPU的佔有率,佔用內存大小.簡單來說一個運行的程序就是進程.
  • 這裏給出一個定義: 一個具有獨立功能的程序在一個數據集合上的一次動態執行
    也就是說把一個可執行文件(程序)加載到內存中,CPU執行的這個動態過程稱爲進程.
  • 我們可以用多個進程來完成一件任務,但是進程間的通訊,共享數據實現比較的麻煩,同時系統維護進程的開銷也比較的大(例如 分配資源,建立PCB,撤銷進程,回收資源,撤銷PCB,進程的切換),爲了解決這些問題,提出了線程. 將進程的責任進行進一步的劃分.進程來執行管理功能.將進程看做是一個資源的平臺(環境),提供各種的資源(地址空間,打開的文件,數據段,代碼段).而進程的執行功能,交給線程.``線程的定義:進程中的一條執行流程 線程可以訪問進程的資源,那麼多個線程完成一個任務,比多個進程來完成實現簡單.
  • 線程=進程- 共享資源
    線程的優點 :
  1. 一個進程多以同時存在多個線程,多個執行流
  2. 線程可以併發的執行
  3. 每個線程直接共享地址空間和文件資源
    線程的缺點 :
  4. 一個線程崩潰,會導致所屬的進程的所有線程崩潰.
    併發和並行:
  • 併發:以前的計算機只有一個CPU中只有一個執行單元,任何的時刻只能執行一條代碼,但是速度足夠的快,我們可以不斷的切換進程(線程),(雖然只有一個進程(線程)在運行,但是速度足夠的快,我們在使用的過程中感覺不到進程(線程)的切換,彷彿同時執行多個任務.也就是說在一段時間內,它們是同時執行的.

  • 並行 :現在的CPU中有多個執行單元,相當於有多個流水線,可以同一時刻執行多條指令.同一時刻,可以同時處理對個進程(線程).

  • 多線程(進程)究竟是併發還是並行是操作系統的調度來確定的,程序編寫者無法確定.但是,併發還是並行它們的最終的結果還是不變的.

  • 雖然說Java的多線程與具體和平臺無關,但是要明白Java多線程,還是需要了解一下操作系統中線程和進程相關的知識.

二.Java多線程的入門

Java實現多線程有四種機制:

  • 繼承Thread
  • 實現Runable接口
  • 通過CallableFuture創建
  • 使用線程池創建

繼承Thread

步驟:

  1. 定義一個繼承Thread類的子類.並重寫該類的run()方法
  2. 創建Thread子類的實例,即創建了線程對象
  3. 調用該線程對象的start()方法,啓動線程
public class MyThread extends Thread{
	

@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i= 0;i<10;i++) {
			System.out.println(Thread.currentThread().getName()+"  "+i); //currentThread()是一個靜態方法,獲得當前執行的線程.
		}
	}

public static void main(String[] args) {
	Mythread myThread = new ThreadLocalDemo();
	 myThread.start(); //啓動線程
	for(int i= 0;i<10;i++) {
		System.out.println(Thread.currentThread().getName()+"  "+i);
	}
}
}

main  0
main  1
main  2
main  3
Thread-0  0
main  4
Thread-0  1
Thread-0  2
main  5
Thread-0  3
Thread-0  4
Thread-0  5
Thread-0  6
Thread-0  7
main  6
Thread-0  8
Thread-0  9
main  7
main  8
main  9

實現Runable接口

步驟:

  1. 定義Runnable接口的實現類,並重寫該接口的run()方法
  2. 創建Runnable實現類的實例
  3. 並以此實例作爲Thread的target對象傳入.
  4. 啓動該線程的thread的實現類
public class MyThread implements Runnable {

	@Override
	public void run() {
		for(int i= 0;i<10;i++) {
			System.out.println(Thread.currentThread().getName()+"  "+i); 
		}
	}

public static void main(String[] args) {
	MyThread myThread= new MyThread();
	Thread thread1= new Thread(myThread);
	thread1.start();
	for(int i= 0;i<10;i++) {
		System.out.println(Thread.currentThread().getName()+"  "+i); 
	}
}
}

通過CallableFuture創建

這種方法可以使得線程執行有返回值

  • 創建Callable接口的實現類,並實現call()方法,該call()方法將作爲線程執行體,並且有返回值。
  • 創建Callable實現類的實例,使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的返回值。
  • 使用FutureTask對象作爲Thread對象的target創建並啓動新線程。
  • 調用FutureTask對象的get()方法來獲得子線程執行結束後的返回值其中.
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
	// TODO Auto-generated method stub
	for(int i =0;i<10;i++) {
		System.out.println(Thread.currentThread().getName()+"  "+i);
	}
	return "執行完成";
}
public static void main(String[] args) {
	FutureTask task = new  FutureTask<String>( new  MyCallable());
	Thread thread1 = new Thread(task,"myThread");
	thread1.start();
	for(int i =0;i<10;i++) {
		System.out.println(Thread.currentThread().getName()+"  "+i);
	}
    y {
		System.out.println(task.get()); //get方法值阻塞的.除非等待task中的線程執行完成
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (ExecutionException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}
}

使用線程池創建

日後在補

三. 線程的生命週期

在這裏插入圖片描述

  1. 新建狀態
    用new創建一個Tread實例後,在線程便處於新生狀態,此時線程已經擁有自己的棧,應該還擁有類似一份TCB的數據結構,維護自己線程信息的結構.
  2. 就緒態
    此時線程已經具備可執行的條件,但是沒有獲得CPU,出入線程就緒隊列中,等待系統爲其分配CPU。等待狀態並不是執行狀態,當系統選定一個等待執行的Thread對象後,它就會從等待執行狀態進入執行狀態,系統挑選的動作稱之爲“cpu調度”。一旦獲得CPU,線程就進入運行狀態,繼續執行代碼
  3. 運行狀態
    程序獲得CPU的執行權,執行指令. 此時線程可能會進入阻塞狀態或者線程死亡
    進入阻塞狀態的分類:
  • 主動的放棄CPU(調用Sleep())
  • 線程調用一個阻塞式I/O,在I/O方法返回前,線程都是出於阻塞態
  • 線程試圖獲得一個互斥資源,而該資源被其他線程佔用
  • 線程在等待某個通知
  • 線程調用suspend()方法.當時該方法容易導致死鎖.
    當線程的run()方法執行完,或者被強制性地終止,例如出現異常,或者調用了stop()、destory()方法等等,就會從運行狀態轉變爲死亡狀態。

四 線程的管理

  1. sleep() 線程睡眠
    如果我們需要讓當前正在執行的線程暫停一段時間,並進入阻塞狀態,則可以通過調用Thread的sleep方法。sleep是一個靜態方法,調用該方法會使當前的線程阻塞.java線程是有JVM的調度算法決定的,我們不可能精確的控制,sleep()阻塞1s後,進入就緒態,當時不保證立馬被調用.
  2. yield()線程讓步
    也是Thread中的一個靜態方法,作用是讓當前的線程進入到就緒狀態.yield()方法只是讓當前線程暫停一下,重新進入就緒的線程池中,讓系統的線程調度器重新調度器重新調度一次,完全可能出現這樣的情況:當某個線程調用yield()方法之後,線程調度器又將其調度出來重新進入到運行狀態執行。
    sleep和yield的區別:
  • sleep方法會使暫停當前線程後,會進入阻塞狀態,只有當睡眠時間到了,纔會轉入就緒狀態。而yield方法調用後 ,是直接進入就緒狀態,所以有可能剛進入就緒狀態,又被調度到運行狀態。
  • sleep方法聲明拋出了InterruptedException,所以調用sleep方法的時候要捕獲該異常,或者顯示聲明拋出該異常。而yield方法則沒有聲明拋出任務異常。
  • sleep方法比yield方法有更好的可移植性,通常不要依靠yield方法來控制併發線程的執行。
  1. join() 線程合併
    線程的合併就將幾個併發的線程合併成一個"單線程執行".應用場景是當一個線程必須等待另一個線程執行完畢才能執行,join()方法不是靜態的
    join的三個重載方法 :
void join()
//當前線程等待該線程終止的時間最長爲 millis 毫秒。 如果在millis時間內,該線程沒有執行完,那麼當前線程進入就緒狀態,重新等待cpu調度  
void join(long mills)
  // 等待該線程終止的時間最長爲 millis 毫秒 + nanos 納秒。如果在millis時間內,該線程沒有執行完,那麼當前線程進入就緒狀態,重新等待cpu調度  
void join(long mills, int nanos) 
  1. 設置線程的優先級
    在算法調度中,並不是簡單的先進先出隊列來實現調度機制.通常每個線程都有一個優先級屬性,優先級屬性越高,獲得執行的機會就越多,而優先級低的線程則獲得較少的執行機會。與線程休眠類似,線程的優先級仍然無法保障線程的執行次序。只不過,優先級高的線程獲取CPU資源的概率較大,優先級低的也並非沒機會執行。
    Thread類提供了setPriority(int newPriority)和getPriority()來 設置優先級,雖然Java設置是1-10,當時對應操作系統劃分的等級卻不一定,所以JAVA用三個靜態常量來實現.
MAX_PRIORITY   =10;
MIN_PRIORITY   =1;
NORM_PRIORITY   =5;
  1. 後臺守護線程
    守護線程使用的情況比較的少. 例如JVM的垃圾,內存管理等線程都是守護線程,守護線程的用途通常用來執行一些後臺作業,例如運行程序的時候播放音樂,文字編輯器裏的自動語法檢查.守護線程的好處,不需要關心它的結束問題.只有JVM虛擬機退出的時候才退出.
public static void setDeamom(boolean on) //將該線程標記爲守護線程或者用戶線程,當正在運行的線程都是守護線程時,JVM退出
// on -true,則將該線程標記爲守護線程  
// 拋出: 
// IlleagalThreadStateException  如果該線程處於活動態 
// SecurityException 如果當前線程無法修改

注:JRE判斷程序是否執行結束的標準是所有的前臺執線程行完畢了,而不管後臺線程的狀態,因此,在使用後臺線程時候一定要注意這個問題。
6. 正確的執行線程
Thread.stop()、Thread.suspend、Thread.resume、Runtime.runFinalizersOnExit這些終止線程運行的方法已經被廢棄了,使用它們是極端不安全的!想要安全有效的結束一個線程,可以使用下面的方法:

• 正常執行完run方法,然後結束掉;

• 控制循環條件和判斷條件的標識符來結束掉線程。
class MyThread extends Thread {  
    int i=0;  
    boolean next=true;  
    @Override  
    public void run() {  
        while (next) {  
            if(i==10)  
                next=false;  
            i++;  
            System.out.println(i);  
        }  
    }  
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章