多線程 1、程式、進程、線程的理解 1.程式(program) 概念:是為了完成特定任務、用某種語言編寫的一組指令的集合。即指一段靜態的代碼塊。 2.線程(process) 概念:程式的一次執行過程,或是正在運行的一個程式。 說明:進程作為資源分配的單位,系統在運行時會為每個進程分配不同的記憶體區域。 ...
多線程
1、程式、進程、線程的理解
1.程式(program)
概念:是為了完成特定任務、用某種語言編寫的一組指令的集合。即指一段靜態的代碼塊。
2.線程(process)
概念:程式的一次執行過程,或是正在運行的一個程式。
說明:進程作為資源分配的單位,系統在運行時會為每個進程分配不同的記憶體區域。
3.線程(thread)
概念:進程可進一步細化為線程,是一個程式內部的一條執行路徑。
說明:線程作為調度和執行的單位,每個線程擁有獨立的運行棧和程式計數器(pc),線程切換的開銷小。
共用同一個進程中的結構:方法區、堆。
2、並行和併發
1.單核CPU與多核CPU的理解
1)單核CPU,其實是一種假的多線程,因為在一個時間單元內,也只能執行一個線程的任務。例如:雖然有多車道,但是收費站只有一個工作人員在收費,只有受了費才能通過,那麼CPU就好比收費人員。如果某個人不想交錢,那麼收費人員可以把他“掛起”(晾著他,等他想通了,準備好了錢,再去收費。)但是因為CPU時間單元特別短,因此感覺不出來。
2)如果是多核的話,才能更好的發揮線程的效率。(現在的伺服器都是多核的)
3)一個Java應用程式java.exe,其實至少3個線程,main()主線程,gc()垃圾回收線程,異常處理線程。當然如果發生異常,會影響主線程。
2.並行與併發的理解
1)並行:多個CPU同時執行多個任務。比如:多個人同時做不同的事。
2)併發:一個CPU(採用時間片)同時執行多個任務。比如:秒殺、多個人同做一件事。
3、創建多線程的兩種方式
-
方式一:繼承Thread類的方式:
1)創建一個繼承於Thread類的子類。
2)重寫Thread類的run()-->將此線程執行的操作聲明在run()中。
3)創建Thread類的子類對象
4)通過此對象調用start():1.啟動當前線程;2.調用當前線程的run()。
說明兩個問題:
問題一:我們啟動的線程,必須調用start(),不能調用run()方法啟動線程。
問題二:如果再啟動一個線程,必須重新創建一個Thread子類的對象,調用此對象的start()。
-
方式二:實現Runnab()介面的方式:
1)創建一個是實現了Runnable()介面的類。
2)實現類去實現Runnable中的抽象方法:run()
3)創建類的實現對象。
4)將此對象作為參數傳遞到Thread類的構造器中,創建Thread類的對象。
5)通過Thread類的對象調用start()
兩種方式的對比:
1.開發中,優先選擇:實現Runnable介面的方式。
2.原因:1)實現的方式沒類的單繼承性的局限性。
2)實現的方式更適合來處理多個線程共用數據的情況。
聯繫:
public class Thread implements Runnable
相同點:兩種方式都需要重寫run() ,將線程要執行的邏輯聲明在run()中。目前兩種方式,要想啟動線程,都是調用的Thread類中的start()。
4、Thread類中的常用方法:
1.start():啟動當前線程;調用當前線程的run()。
2.run():通常需要重寫Thread類中的此方法,將創建的線程要執行的操作聲明在此方法中。
3.currentThread():靜態方法,返回執行當前代碼的線程。
4.getName():獲取當前線程的名字。
5.setName():設置當前線程的名字。
6.yield():釋放當前cpu的執行權。
7.join():線上程a中調用線程b的join(),此時線程a就進入阻塞狀態,直到線程b完全執行完以後,線程a才結束阻塞狀態。
8.stop():已過時。當執行此方法時,強制結束當前線程。
9.sleep(long millitime) :讓當前線程“睡眠”指定的millitime毫秒時間內,當前線程是阻塞狀態。
10.isAlive():判斷當前線程是否存活。
線程的優先順序:
-
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5 -->預設優先順序
-
如何獲取和設置當前線程的優先順序:
getPriority():獲取線程的優先順序
setPriority(int p):設置線程的優先順序
說明:高優先順序的線程要搶占低優先順序的cpu的執行權。但是只是從概率上講,高優先順序的線程高概率的情況下被執行,並不意味著只當高優先順序的線程執行完以後,低優先順序的線程才執行。
線程通信:wait() / notify() / notifyAll():此三種方法定義在Object類中的。
補充:線程的分類
一種是守護線程,一種是用戶線程。
5、Thread的生命周期
JDK中的Thread.State類中定義了線程的幾種狀態
新建
就緒
運行
阻塞
死亡
6、線程的同步機制
在Java中,我們通過同步機制,來解決線程的安全問題。
補充:在實現Runnable介面創建多線程的方式中,我們可以考慮使用this充當同步監視器。
在繼承Thread類創建多線程的方式中,慎用this充當同步監視器,考慮使用當前類充當同步監視器。
-
方法一:同步代碼塊
synchronized(同步監視器){
//需要被同步的代碼
}
說明:1.操作共用數據的代碼,即為需要被同步的代碼。
2.共用數據:多個線程共同操作的變數。
3.同步監視器,俗稱:鎖。任何一個類的對象,都是可以充當鎖。要求:多個線程必須要共用同一把鎖。
-
方法二:同步方法
-
同步方法仍然涉及到同步監視器,只是不需要我們顯式的聲明。
-
非靜態的同步方法,同步監視器是:this
靜態的同步方法,同步監視器是:當前類本身
-
方式三:Lock鎖 --- JDK5.0新增
synchronized 與 lock 的異同:
相同:二者都可以解決線程安全問題
不同:synchronized機制在執行完相應的同步代碼以後,自動的釋放同步監視器
Lock需要手動的啟動同步( Lock() ),同時結束同步也需要手動的實現( unlock() )。
7、線程通信
例子:使用兩個線程列印1-100,線程1,線程2,交替列印
涉及到三個方法:
wait():一旦執行此方法,當前線程就進入阻塞狀態,並釋放同步監視器。
notify():一旦執行此方法,就會喚醒被wait的一個線程。如果有多個線程被wait,就喚醒優先順序高的。
notifyAll():一旦執行此方法,就會喚醒所有被wait的線程。
說明:
1.wait(),notify(),notifyAll()三個方法必須使用在同步代碼塊或同步方法中。
2.wait(),notify(),notifyAll()三個方法的調用者必須是同步代碼塊或同步方法塊中的同步監視器。
3.wait(),notify(),notifyAll()三個方法是定義在java.lang.Object類中的。
sleep()和wait()的異同?
1.相同點:一旦執行方法,都可以使得當前的線程進入阻塞狀態。
2.不同點:1) 兩個方法聲明的位置不同:Thread類中聲明sleep(),Object()類中聲明wait()。
2)調用的要求不同:sleep()可以在任何需要的場景下調用,wait()必須使用在同步代碼或同步方中。
3)關於是否釋放同步同步監聽器:如果兩個方法都使用在同步代碼塊或同步方法中 ,sleep()不會釋放 鎖,wait()會釋放鎖。
生產者消費者問題:
class Clerk{
private int productCount = 0;
public synchronized void produceProduct() {
if(productCount<20){
productCount++;
System.out.println(Thread.currentThread().getName()+":開始生產第"+productCount+"個產品");
notify();
}else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void consumeProduct() {
if(productCount>0){
System.out.println(Thread.currentThread().getName()+":開始消費第"+productCount+"個產品");
productCount--;
notify();
}else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//生產者
class Producer extends Thread{
private Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}