1、進程
是一個執行中的程式,每一個進程執行都有一個執行順序,該執行順序是一個執行路徑,或者叫一個控制單元。
線程:就是進程中的一個獨立的控制單元,線程式控制制著進程的執行。一個進程中至少有一個線程。
Java jvm啟動的時候會有一個進程java.exe,該進程中至少一個線程負責java程式的執行,而且這個線程啟動並執行代碼存在於main方法中,該線程稱之為主線程。
java jvm啟動還會啟動一個線程,負責記憶體回收機制的線程。
如何在自訂的代碼中,自訂一個線程?線程2種方式:
建立線程的第一種方式:繼承Thread類。
步驟:
第一、定義類繼承Thread。
第二、覆寫Thread類中的run方法。目的是將自訂的代碼寫入run方法中。
第三、調用線程的start方法,該方法有兩個作用,啟動線程、調用run方法。
run方法和start方法不同點:
run方法僅僅是對象調用run方法,建立了線程,但是線程並沒有運行。
start方法是開啟了線程,並執行該線程的run方法。
class Demo extends Thread{ public void run(){ for(int x=0;x<50;x++){ System.out.println("demo run--->"+x); } }}public class ThreadDemo01 { public static void main(String[] arg){ Demo d= new Demo(); d.start(); for(int x=0;x<50;x++){ System.out.println("ThreadDemo run===>"+x); } }}
建立線程的第二種方式:實現Runnable介面
步驟:
第一、定義類來實現Runnable介面;
第二、覆蓋Runnable介面中的run方法,將線程要啟動並執行代碼存放在該run方法中。
第三、通過Thread類建立線程對象。
第四、將Runnable介面的子類對象作為實際參數傳遞給Thread的建構函式。因為自訂的run方法所屬的對象是Runnable介面的子類對象,所以要讓線程去指定對象的run方法,就必須明確該run方法所屬對象。
第五、調用Thread類的start方法開啟線程病調用Runnable介面子類的run方法。
class Demo03 implements Runnable{ private int tick = 100; public void run(){ while(true){ if(tick>0){ System.out.println(Thread.currentThread().getName()+"..sale.."+tick--); } } }}public class ThreadDemo03 { public static void main(String[] args){ Demo03 d = new Demo03(); Thread t = new Thread(d); t.start(); }}
實現方式和繼承方式區別:
一般用實現方式,因為實現方式可以避免繼承的單繼承缺點。
2、線程的運行狀態
建立:new一個thread對象或者其子類對象就是建立一個線程。
就緒:建立的對象調用了start方法。
運行:對象的運行方法定義在run中。
凍結:sleep、wait(notify)。
死亡:stop。
3、擷取線程對象及名稱
線程都有自己預設的名稱,Thread-編號,該編號從0開始。Static Thread currentThread()擷取當前線程對象。getName()擷取線程名稱。設定線程名稱:setName或者建構函式。
4、多線程的安全問題
多線程出現安全問題的原因:當多條語句在操作同一個線程共用資料的時候,一個線程對多條語句只執行了一部分,還沒有執行完,另一個線程就參與進來執行,導致共用資料的錯誤。
解決多安全執行緒問題方法:多多條操作共用資料的語句,只能讓一個線程都執行完。在執行過程中,其他線程不可以參與進來。
java對於多線程的安全問題提供了專業的解決辦法:
方法一、同步代碼塊。
synchronized(對象){
需要被同步的代碼塊
}
對象如同鎖,持有鎖的線程可以在同步代碼塊中執行,沒有持有鎖的線程即使擷取cpu的執行權,也進不去,因為沒有擷取鎖。
同步的前提:
第一、必須要有兩個或者以上的線程; 第二、必須是多個線程使用同一個鎖。
第三、必須保證同步中只能有一個線程在運行。
同步的好處:解決了多線程的安全問題。
同步的弊端:多個線程需要判斷鎖,比較消耗資源。
方法二、同步函數
函數傳回值類型 synchronized 函數名(..){…}
如何找需要同步的代碼
第一、明確哪些代碼是多線程運行代碼;
第二、明確共用資料;
明確多線程運行代碼中哪些語句是操作共用資料的。
5、同步函數的鎖是this
同步函數用的是哪一個鎖?函數需要被對象調用,那麼函數都有一個所屬對象引用,就是this,所以同步函數使用的鎖是this。
class Ticket implements Runnable{ private int ticket = 100; Object obj = new Object(); boolean flag = true; public void run(){ if(flag){ while(true){ synchronized(this){//同步代碼塊用的鎖自己可以定義一個對象 if(ticket>0){ try{Thread.sleep(10);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"..sale.."+ticket--); } } } }else{ while(true){ show(); } } } public synchronized void show(){//同步函數用的鎖為this if(ticket>0){ try{Thread.sleep(10);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"****show****"+ticket--); } }}public class ThreadDemo{ public static void main(String[] args){ Ticket t = new Ticket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); try{Thread.sleep(10);}catch(Exception e){} t.flag = false; t2.start(); } }
6、靜態同步函數的鎖是Class對象
如果同步函數被靜態修飾後,使用的鎖不再是this,而是Class對象。因為靜態方法中不可以定義this,當靜態函數進記憶體時,記憶體中沒有本類對象,但有本類對應的位元組碼檔案對象,就是類名.clss,該對象的類型是Class。
class Ticket implements Runnable{ private static int ticket = 100; boolean flag = true; public void run(){ if(flag){ while(true){ synchronized(Ticket.class){//靜態同步方法鎖是本類位元組碼對象 if(ticket>0){ try{Thread.sleep(10);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"..sale.."+ticket--); } } } }else{ while(true){ show(); } } } public static synchronized void show(){//靜態同步函數用的鎖為Ticket.class位元組碼對象,即Class對象。 if(ticket>0){ try{Thread.sleep(10);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"****show****"+ticket--); } }}public class ThreadDemo{ public static void main(String[] args){ Ticket t = new Ticket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); try{Thread.sleep(10);}catch(Exception e){} t.flag = false; t2.start(); } }
7、單例設計模式
單例設計思想:
為了其他程式過多建立該類對象,禁止其他程式建立該類對象---將建構函式私人化。
為了讓其他程式能訪問到該類對象,在本類中定義一個對象。---》在類中建立一個本類對象。
為了方便其他程式對自訂兌現的訪問,可以對外提供一些訪問方式---》提供一個方法可以擷取該對象。
第一、餓漢式
class SingleDemo{ private SingleDemo(){} private static final SingleDemo s = new SingleDemo(); public static SingleDemo getInstance(){ return s; }}
第二、懶漢式
class Single02{ private Single02(){} private static Single02 instance = null; public static Single02 getInstance(){ if(instance==null){//雙重否定加同步 synchronized(Single02.class){//鎖為該類所屬的位元組碼檔案 if(instance==null){ instance = new Single02(); } } } return instance; }}
8、死結
a鎖調用b鎖,b鎖調用a鎖,迴圈至死。
總結:
1、實現線程的兩種方式:繼承Thread類,實現Runnable介面。
2、單例設計模式中餓漢式使用了同步避免bug。