java執行緒
阿新 • • 發佈:2019-12-31
概述
相關關係:
- 程式:可理解為一組靜態程式碼。
- 程式:正在進行的程式(靜態的程式碼,執行起來)。
- 執行緒:正在執行程式中的小單元。
執行緒:
1、主執行緒 :系統執行緒
2、使用者執行緒 :main
3、守護執行緒(精靈):比如GC(垃圾回收)
複製程式碼
執行緒狀態:
1、建立執行緒(new)
2、就緒狀態(start())
3、執行狀態(CPU分配run)
4、等待/掛起(wait)
5、-----異常/死亡(exception over)/(或重新回到就緒狀態)(notify/notifyAll--喚醒)
複製程式碼
基本操作
一 、建立執行緒
-
方式一:繼承Thread類
1、 定義繼承Thread; 2、複寫Thread類中的run方法;(目的:將自定義程式碼儲存在run方法中,讓執行緒執行(同時執行,搶奪資源)) 3、呼叫執行緒的start方法;(目的:啟動執行緒,呼叫run方法) 複製程式碼
-
方式二:實現Runnable介面
1、定義類實現Runnable介面; 2、覆蓋Runnable介面中的run方法; 3、通過Thread類建立執行緒物件; 4、將Runnable介面的子類物件作為實際引數傳遞給Thread類的構造方法; 5、呼叫Thread類的start方法,開啟執行緒並調節Runnable介面子類的run方法; 複製程式碼
實現方式和繼承方式的區別:
- 繼承只能繼承一個,具有侷限性。
- 繼承Thread: 執行緒程式碼存放在子類run。
- 實現runnable: 執行緒程式碼存放在介面的子類run。
應用舉例:
-
方式一:
//1、繼承Thread class Test extends Thread{ private String name; Test(String name) { this.name=name; } public void run(){ //2、run方法 for(int x=0;x<60;x++) { System.out.println(name+"----"+x); } } } public class TestMain { public static void main(String[] args) { Test q1=new Test("lisi"); Test q2=new Test("lisan"); q1.start(); //3、呼叫start q2.start(); } } 複製程式碼
-
方式二:
- class Ticket implements Runnable//1、實現Runnable介面 { private int tick=100; public void run()//2、run方法 { while(true) { if(tick>0) System.out.println(Thread.currentThread().getName()+"...sale:"+tick--); } } } class TestMain { public static void main(String[] args) { Ticket t=new Ticket(); //3、建立執行緒 Thread t1=new Thread(t);//4、傳遞引數 Thread t2=new Thread(t); Thread t3=new Thread(t); Thread t4=new Thread(t); //5、呼叫start t1.start(); t2.start(); t3.start(); t4.start(); } } 複製程式碼
二、獲取執行緒物件及名稱
- 獲取當前執行緒物件:Thread.currentThread()
- 獲取執行緒名稱:getName()
多執行緒的安全問題:
原因:
當多條語句在操作一個共享資料,一個資料對多條語句只執行了一部分, 還未執行完時,另一個參與進來,導致錯誤。
解決方法:
-
同步程式碼塊:對於多條共享的語句,讓一個執行緒先執行完。
synchronized(物件)//相當於“鎖” { 需要被同步的程式碼 } 複製程式碼
多執行緒-同步函式
步驟:
1、明確哪些程式碼是多執行緒執行程式碼;
2、明確共享資料;
3、明確多執行緒執行程式碼中哪些語句是操作共享資料的;
多執行緒同步函式的鎖是this:
函式需要被物件呼叫,那麼函式都有一個所屬物件引用,即this,所以同步函式的鎖就是this.
死鎖:(死鎖程式要會寫)
產生原因:同步中巢狀同步,鎖卻不同。
單例設計模式:
單例模式的定義與特點:
單例(Singleton)模式的定義:指一個類只有一個例項,且該類能自行建立這個例項的一種模式。
例如,Windows中只能開啟一個工作管理員,這樣可以避免因開啟多個工作管理員視窗而造成記憶體資源的浪費,或出現各個視窗顯示內容的不一致等錯誤。
在計算機系統中,還有Windows的回收站、作業系統中的檔案系統、多執行緒中的執行緒池、顯示卡的驅動程式物件、印表機的後臺處理服務、應用程式的日誌物件、資料庫的連線池、網站的計數器、Web應用的配置物件、應用程式中的對話方塊、系統中的快取等常常被設計成單例。
單例模式有 3 個特點:
1、單例類只有一個例項物件;
2、該單例物件必須由單例類自行建立;
3、單例類對外提供一個訪問該單例的全域性訪問點;
程式碼實現:
//餓漢式。
/*
class Single
{
private static final Single s = new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}
*/
//懶漢式(延遲載入)
class Single
{
private static Single s = null;
private Single(){}
public static Single getInstance()
{
if(s==null)//雙重判斷。提高效率
{
synchronized(Single.class)//靜態沒有this,鎖是該類所屬的位元組碼類物件
{
if(s==null)
//--->A;
s = new Single();
}
}
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
複製程式碼
執行緒間通訊:
為瞭解決安全問題。
多個執行緒操作同一個資源,但是操作的動作不同。
程式碼演示:
class Res
{
String name;
String sex;
boolean flag = false;
}
class Input implements Runnable
{
private Res r ;
Input(Res r)
{
this.r = r;
}
public void run()
{
int x = 0;
while(true)
{
synchronized(r)
{
if(r.flag)
try{r.wait();}catch(Exception e){}
if(x==0)
{
r.name="mike";
r.sex="man";
}
else
{
r.name="麗麗";
r.sex = "女女女女女";
}
x = (x+1)%2;
r.flag = true;
r.notify();
}
}
}
}
class Output implements Runnable
{
private Res r ;
Output(Res r)
{
this.r = r;
}
public void run()
{
while(true)
{
synchronized(r)
{
if(!r.flag)
try{r.wait();}catch(Exception e){}
System.out.println(r.name+"...."+r.sex);
r.flag = false;
r.notify();
}
}
}
}
class InputOutputDemo
{
public static void main(String[] args)
{
Res r = new Res();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
//notifyAll();
--------------------------------------------------
都使用在同步中,因為要對持有監視器(鎖)的執行緒操作。
所以要使用在同步中,因為只有同步才具有鎖。
**為什麼這些操作執行緒的方法要定義Object類中呢?**
因為這些方法在操作同步中執行緒時,都必須要標識它們所操作執行緒只有的鎖,
只有同一個鎖上的被等待執行緒,可以被同一個鎖上notify喚醒。
不可以對不同鎖中的執行緒進行喚醒。
也就是說,等待和喚醒必須是同一個鎖。
而鎖可以是任意物件,所以可以被任意物件呼叫的方法定義Object類中。
複製程式碼
等待喚醒機制:
wait() :等待,將正在執行的執行緒釋放其執行資格 和 執行權,並儲存到執行緒池中。
notify():喚醒,喚醒執行緒池中被wait()的執行緒,一次喚醒一個,而且是任意的。
notifyAll(): 喚醒全部:可以將執行緒池中的所有wait() 執行緒都喚醒。
複製程式碼
生產者、消費者:
對於多個生產者和消費者?
為什麼要定義while判斷標記?
原因:讓被喚醒的執行緒再一次判斷標記。
為什麼定義notifyAll?
因為需要喚醒對方執行緒。
因為只用notify,容易出現只喚醒本方執行緒的情況。導致程式中的所有執行緒都等待。
複製程式碼
停止執行緒:
一般情況:
run方法結束(開啟多執行緒執行,執行程式碼通常是迴圈結構,所以控制迴圈即可)
特殊情況:
當執行緒處於了凍結狀態。
就不會讀取到標記。那麼執行緒就不會結束。
當沒有指定的方式讓凍結的執行緒恢復到執行狀態是,這時需要對凍結進行清除。
強制讓執行緒恢復到執行狀態中來。這樣就可以操作標記讓執行緒結束。
Thread類提供該方法 interrupt();
複製程式碼
守護執行緒:(set .Deamon)
[注意:]啟用前呼叫;執行緒中只剩守護執行緒時,java退出虛擬機器器
複製程式碼
Join方法:
等待執行緒終止:
當A執行緒執行到了B執行緒的.join()方法時,A就會等待。等B執行緒都執行完,A才會執行。
join可以用來臨時加入執行緒執行。
複製程式碼
優先順序:yeild方法
即搶資源的頻率,最高10,預設5