執行緒
Java使用 java.lang.Thread 類代表執行緒,所有的執行緒物件都必須是Thread類或其子類的實體
Thread類常用方法
構造方法
-
public Thread():分配一個新的執行緒物件,
-
public Thread(String name):分配一個指定名字的新的執行緒物件,
-
public Thread(Runnable target):分配一個帶有指定目標新的執行緒物件,
-
public Thread(Runnable target,String name):分配一個帶有指定目標新的執行緒物件并指定名字,
常用方法
-
public string getName():獲取當前執行緒名稱,
-
public void start():導致此執行緒開始執行;Java虛擬機呼叫此執行緒的run方法,
-
public void run():此執行緒要執行的任務在此處定義代碼,
-
public static void sleep(long millis):使當前正在執行的執行緒以指定的毫秒數暫停(暫時停止執行),
-
public static Thread currentThread():回傳對當前正在執行的執行緒物件的參考,
創建執行緒方式一
Java中通過繼承Thread類來創建并啟動多執行緒的步驟如下:
-
定義Thread類的子類,并重寫該類的run()方法,該run()方法的方法體就代表了執行緒需要完成的任務,因此把 run()方法稱為執行緒執行體,
-
創建Thread子類的實體,即創建了執行緒物件
-
呼叫執行緒物件的start()方法來啟動該執行緒
測驗類:
public class Demo{ public static void main(String[] args) { // 創建自定義執行緒物件 MyThread mt = new MyThread("新執行緒"); // 開啟新執行緒 mt.start(); // 在主方法中執行for回圈 for (int i = 0; i < 10; i++) { System.out.println("main執行緒正在執行" + i); } } }
自定義執行緒類:
public class MyThread extends Thread { // 定義指定執行緒名稱的構造方法 public MyThread(String name) { // 呼叫父類的String引數的構造方法,指定執行緒的名稱 super(name); } /** * 重寫run方法,完成該執行緒執行的邏輯 */ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(getName() + "正在執行" + i); } } }
流程圖:

程式啟動運行main時候,java虛擬機啟動一個行程,主執行緒main在main()呼叫時候被創建,
隨著呼叫mt的物件的start方法,另外一個新的執行緒也啟動了,這樣,整個應用就在多執行緒下運行,
創建執行緒方式二
Java中通過實作Runnable介面來創建并啟動多執行緒的步驟如下:
-
定義Runnable介面的實作類,并重寫該介面的run () 方法,該run () 方法的方法體同樣是該執行緒的執行緒執行體,
-
創建Runnable實作類的實體,并以此實體作為Thread的target來創建Thread物件,該Thread物件才是真正的執行緒物件,
-
呼叫執行緒物件的start () 方法來啟動執行緒,
測驗類:
public class Demo { public static void main(String[] args) { // 創建自定義類物件執行緒任務物件 MyRunnable mr = new MyRunnable(); // 創建執行緒物件 Thread t = new Thread(mr, "新執行緒"); t.start(); for (int i = 0; i < 10; i++) { System.out.println("main執行緒正在執行" + i); } } }
自定義執行緒類:
public class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "正在執行" + i); } } }
通過實作Runnable介面,使得該類有了多執行緒類的特征,run () 方法是多執行緒程式的一個執行目標,所有的多執行緒代碼都在run方法里面,Thread類實際上也是實作了Runnable介面的類,
Thread和Runnable的區別
如果一個類繼承Thread,則不適合資源共享,但是如果實作了Runable介面的話,則很容易的實作資源共享,
實作Runnable介面比繼承Thread類所具有的優勢:
-
適合多個相同的程式代碼的執行緒去共享同一個資源,
-
可以避免java中的單繼承的局限性,
-
增加程式的健壯性,實作解耦操作,代碼可以被多個執行緒共享,代碼和資料獨立,
-
執行緒池只能放入實作Runable或callable類執行緒,不能直接放入繼承Thread的類,
匿名內部類方式實作執行緒的創建
public class Demo { public static void main(String[] args) { //使用匿名內部類方法;直接創建Thread類的子類物件 /* * new Thread() { public void run() { for (int i = 0; i < 10; i++) { * System.out.println("新執行緒正在執行" + i); } } }.start(); */ //使用匿名內部類方式;直接創建Runnable介面實作類物件 Runnable r = new Runnable() { public void run() { for (int i = 0; i < 10; i++) { System.out.println("新執行緒正在執行" + i); } } }; new Thread(r).start(); for (int i = 0; i < 10; i++) { System.out.println("main執行緒正在執行" + i); } } }
執行緒安全
兩個或兩個以上的執行緒在訪問共享資源時,仍然能得到正確的結果則稱之為執行緒安全
模擬賣50張電影票
public class Ticket implements Runnable { private int ticket = 50; // 買票操作 @Override public void run() { // 每個視窗買票操作,視窗永遠開啟 while (true) { if (ticket > 0) { // 使用sleep方法模擬買票 try { Thread.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 獲取當前物件的名字 String name = Thread.currentThread().getName(); System.out.println(name + "正在賣票:" + ticket--); } } } }
測驗類:
public class Demo { public static void main(String[] args) { // 創建執行緒任務物件 Ticket ticket = new Ticket(); // 創建三個視窗賣票 Thread t1 = new Thread(ticket, "視窗一"); Thread t2 = new Thread(ticket, "視窗二"); Thread t3 = new Thread(ticket, "視窗三"); ? // 同時開始賣票 t1.start(); t2.start(); t3.start(); } }
結果出現了這種現象:

這種問題,幾個視窗(執行緒)票數不同步了,稱為執行緒不安全
執行緒同步
當我們使用多個執行緒訪問統一資源的時候,且多個執行緒中對資源有寫的操作,就容易出現執行緒安全問題.
要解決上述多執行緒并發訪問多一個資源的安全性問題,java中提供了同步機制(synchronized)來解決,有三種方式完成同步操作:
-
同步代碼塊
-
同步方法
-
鎖機制
同步代碼塊
同步代碼塊:synchronized關鍵字可以用于方法中的某個區塊中,表示只對這個區塊的資源實行互斥訪問
synchronized(同步鎖){
需要同步操作的代碼
}
同步鎖注意事項
1.鎖物件可以是任意型別,
2.多個執行緒物件要使用同一把鎖,
同步代碼塊實作執行緒安全
public class Ticket implements Runnable { private int ticket = 50; Object lock = new Object(); ? // 買票操作 @Override public void run() { // 每個視窗買票操作,視窗永遠開啟 while (true) { synchronized (lock) { if (ticket > 0) { // 使用sleep方法模擬買票 try { Thread.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 獲取當前物件的名字 String name = Thread.currentThread().getName(); System.out.println(name + "正在賣票:" + ticket--); } } } } }
同步方法
同步方法:使用synchronized修飾的方法,就叫做同步方法保證A執行緒執行該方法的時候,其他執行緒只能在方法外等著
public synchronized void method(){
可能會產生執行緒安全問題的代碼
}
同步方法實作執行緒安全
public class Ticket implements Runnable { private int ticket = 50; // 買票操作 @Override public void run() { // 每個視窗買票操作,視窗永遠開啟 while (true) { sellTicket(); } } // 鎖物件是誰呼叫這個方法就是誰,this public synchronized void sellTicket() { if (ticket > 0) { // 使用sleep方法模擬買票 try { Thread.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 獲取當前物件的名字 String name = Thread.currentThread().getName(); System.out.println(name + "正在賣票:" + ticket--); } } }
Lock鎖
java.util.concurrent.locks.Lock 機制提供了比synchronized代碼塊和synchronized方法更廣泛的鎖定操作,同步代碼塊/同步方法具有的功能Lock都有,除此之外更強大,更體現面向物件, Lock常用方法
-
public void lock():加同步鎖,
-
public void unlock():釋放同步鎖,
Lock鎖實作執行緒安全
public class Ticket implements Runnable { private int ticket = 50; Lock lock = new ReentrantLock(); // 買票操作 @Override public void run() { // 每個視窗買票操作,視窗永遠開啟 while (true) { lock.lock(); if (ticket > 0) { // 使用sleep方法模擬買票 try { Thread.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 獲取當前物件的名字 String name = Thread.currentThread().getName(); System.out.println(name + "正在賣票:" + ticket--); } lock.unlock(); } } ? }
執行緒池
執行緒池:其實就是一個容納多個執行緒的容器,其中的執行緒可以反復使用,省去了頻繁創建執行緒物件的操作,無需反復創建執行緒而消耗過多資源,
執行緒池能夠帶來三個好處:
-
降低資源消耗,減少了創建和銷毀執行緒的次數,每個作業執行緒都可以被重復利用,可執行多個任務,
-
提高回應速度,當任務到達時,任務可以不需要的等到執行緒創建就能立即執行,
-
提高執行緒的可管理性,可以根據系統的承受能力,調整執行緒池中作業線執行緒的數目,防止因為消耗過多的記憶體
執行緒池的使用
Java里面執行緒池的頂級介面是java.util.concurrent.Executors
public static ExecutorService newFixedThreadPool(int nThreads) :回傳執行緒池物件,(創建的是有界執行緒池,也就是池中的執行緒個數可以指定最大數量)
public Future<?> submit(Runnable task) :獲取執行緒池中的某一個執行緒物件,并執行
Runnable實作類代碼:
public class MyRunnable implements Runnable { @Override public void run() { System.out.println("我要一個教練"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("教練來了: " + Thread.currentThread().getName()); System.out.println("教我游泳,交完后,教練回到了游泳池"); } }
執行緒池測驗類:
public class Demo{ public static void main(String[] args) { // 創建執行緒池物件 ExecutorService service = Executors.newFixedThreadPool(2);//包含2個執行緒物件 // 創建Runnable實體物件 MyRunnable r = new MyRunnable(); //自己創建執行緒物件的方式 // Thread t = new Thread(r); // t.start(); ---> 呼叫MyRunnable中的run() // 從執行緒池中獲取執行緒物件,然后呼叫MyRunnable中的run() service.submit(r); // 再獲取個執行緒物件,呼叫MyRunnable中的run() service.submit(r); service.submit(r); // 注意:submit方法呼叫結束后,程式并不終止,是因為執行緒池控制了執行緒的關閉, // 將使用完的執行緒又歸還到了執行緒池中 // 關閉執行緒池 //service.shutdown(); } }
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/122658.html
標籤:Java
