JAVA多執行緒知識彙總
執行緒概念
程序:啟動一個應用程式就叫一個程序。 接著又啟動一個應用程式,這叫兩個程序。每個程序都有一個獨立的記憶體空間;程序也是程式的一次執行過程,是系統執行程式的基本單位;系統執行一個程式即是一個程序從建立、執行到消亡的過程。
執行緒:執行緒是在程序內部同時做的事情,一個程序中可以有多個執行緒,這個應用程式也可以稱之為多執行緒程式。
一個程式執行後至少有一個程序,一個程序中可以包含多個執行緒
執行緒排程:
- 分時排程:所有執行緒輪流使用 CPU 的使用權,平均分配每個執行緒佔用 CPU 的時間。
- 搶佔式排程:優先讓優先順序高的執行緒使用 CPU,如果執行緒的優先順序相同,那麼會隨機選擇一個(執行緒隨機性),Java使用的為搶佔式排程。
建立多執行緒
方法一:建立Thread類的子類
- 建立Thread類的子類,並重寫該類的run()方法,設定執行緒任務。
- 建立Thread子類的例項,即建立了執行緒物件
- 呼叫執行緒物件的start()方法來啟動該執行緒
//方法一: //定義Thread類的子類,並重寫該類的run()方法 public class MyThreadDemo01 extends Thread { @Override public void run() { for (int i = 0; i < 20 ; i++) { System.out.println(getName()+"-->"+i); } } }
//主執行緒 public class MainThread01 { public static void main(String[] args) { //建立Thread子類的例項,即建立了執行緒物件 MyThreadDemo01 thread01 = new MyThreadDemo01(); //呼叫執行緒物件的start()方法來啟動該執行緒 thread01.start(); for (int i = 0; i < 10 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } }
public static Thread currentThread() :返回對當前正在執行的執行緒物件的引用。
public String getName() :獲取當前執行緒名稱。
public void start() :導致此執行緒開始執行; Java虛擬機器呼叫此執行緒的run方法。
public void run() :此執行緒要執行的任務在此處定義程式碼。
public static void sleep(long millis) :使當前正在執行的執行緒以指定的毫秒數暫停(暫時停止執行)。
方法二:實現Runnable介面
- 定義Runnable介面的實現類,並重寫該介面的run()方法,設定執行緒任務
- 建立Runnable實現類物件
- 建立Thread類的物件,並且該物件構造方法中傳遞Runnable實現類物件
- 呼叫Thread物件的start()方法來啟動執行緒
//方法二: //定義Runnable介面的實現類,並重寫該介面的run()方法,設定執行緒任務 public class MyThreadDemo02 implements Runnable{ @Override public void run() { for (int i = 0; i < 10 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } }
//主執行緒 public class MainThread { public static void main(String[] args) { //建立Runnable實現類物件 MyThreadDemo02 runnable = new MyThreadDemo02(); //建立Thread類的物件,並且該物件構造方法中傳遞Runnable實現類物件 Thread thread02 = new Thread(runnable); //呼叫Thread物件的start()方法來啟動執行緒 thread02.start(); for (int i = 0; i < 20 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } }
方法三:匿名內部類方式
- 匿名內部類能夠簡化程式
//方法三:匿名內部類 public class MainThread { public static void main(String[] args) { //Thread方式 new Thread(){ @Override public void run() { for (int i = 0; i < 10 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } }.start(); //Runnable介面方式 new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } }).start(); //////////////////////////////////////////////// for (int i = 0; i < 20 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } }
執行緒安全問題
多執行緒訪問共享資料,,且多個執行緒中對資源有寫的操作,就會出現執行緒安全問題
執行緒安全問題都是由全域性變數及靜態變數引起的。若每個執行緒中對全域性變數、靜態變數只有讀操作,而無寫操作,一般來說,這個全域性變數是執行緒安全的;若有多個執行緒同時執行寫操作,一般都需要考慮執行緒同步, 否則的話就可能影響執行緒安全。
解決執行緒安全問題採用執行緒同步機制,主要有以下三種方式:
- 同步程式碼塊
- 同步方法
- 鎖機制
同步程式碼塊
同步程式碼塊:synchronized 關鍵字可以用於方法中的某個區塊中,表示只對這個區塊的資源實行互斥訪問。
- 格式:synchronized(鎖物件){ //訪問共享資料的程式碼 }
- 鎖物件可以是任意型別
- 多個執行緒物件要使用同一把鎖
- 鎖物件是將同步程式碼塊鎖住,只讓執行緒在同步程式碼塊中執行
public class SafeRunnableDemo implements Runnable { private int ticket = 100; //同步程式碼塊 //建立鎖物件 Object lock = new Object(); @Override public void run() { while (true){ //鎖住同步程式碼塊 synchronized (lock){ if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "正在賣第" + ticket + "張"); ticket--; } } } } }
同步方法
同步方法:使用synchronized修飾的方法,就叫做同步方法,保證A執行緒執行該方法的時候,其他執行緒只能在方法外等著
- 格式:修飾符 synchronized 返回值型別 方法名(引數列表) { //訪問共享資料的程式碼 }
- 把共享了同步資料的程式碼抽取出來,放入同步方法中
public class SafeRunnableDemo implements Runnable { private int ticket = 100; //同步方法 //定義一個同步方法 public synchronized void lock(){ //同步方法鎖住程式碼塊 if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "正在賣第" + ticket + "張"); ticket--; } } //重寫run並使用同步方法 @Override public void run() { while (true){ lock(); } } }
Lock鎖
Lock提供了比synchronized更廣泛的鎖操作
- 在Lock介面中 void lock() 獲取鎖,void unlock() 釋放鎖
- 需要在成員位置處建立ReentraLock物件,在共享資料程式碼塊之前呼叫方法lock()獲取鎖,在之後用unlock()方法釋放鎖
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class SafeRunnableDemo implements Runnable { private int ticket = 100; //Lock鎖方法 //建立ReentrantLock物件 Lock lock = new ReentrantLock(); @Override public void run() { while (true){ //在可能出現問題的程式碼塊之前用lock()方法 lock.lock(); if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "正在賣第" + ticket + "張"); ticket--; } //在可能出現問題的程式碼塊之後用unlock()方法 lock.unlock(); } } }
執行緒機制
- NEW(新建):執行緒剛被建立,但是並未啟動。還沒呼叫start()方法。
- Runnable(可執行):執行緒可以在java虛擬機器中執行的狀態,可能正在執行自己程式碼,也可能沒有,這取決於作業系統處理器。
- Blocked(鎖阻塞):當一個執行緒試圖獲取一個物件鎖,而該物件鎖被其他的執行緒持有,則該執行緒進入Blocked狀態;當該執行緒持有鎖時,該執行緒將變成Runnable狀態。
- Waiting(無限等待):一個執行緒在等待另一個執行緒執行一個(喚醒)動作時,該執行緒進入Waiting狀態。進入這個狀態後是不能自動喚醒的,必須等待另一個執行緒呼叫notify()或者notifyAll()方法才能夠喚醒。
- Timed Waiting(計時等待):同waiting狀態,有幾個方法有超時引數,呼叫他們將進入Timed Waiting狀態。這一狀態 將一直保持到超時期滿或者接收到喚醒通知。帶有超時引數的常用方法有Thread.sleep()、Object.wait()。
- Teminated(被終止):因為run方法正常退出而死亡,或者因為沒有捕獲的異常終止了run方法而死亡。
一個呼叫了某個物件的 Object.wait() 方法的執行緒會等待另一個執行緒呼叫此物件Object.notify()方法 或 Object.notifyAll()方法。
其實waiting狀態並不是一個執行緒的操作,它體現的是多個執行緒間的通訊,可以理解為多個執行緒之間的協作關係, 多個執行緒會爭取鎖,同時相互之間又存在協作關係。
當多個執行緒協作時,比如A,B執行緒,如果A執行緒在Runnable(可執行)狀態中呼叫了wait()方法那麼A執行緒就進入 了Waiting(無限等待)狀態,同時失去了同步鎖。假如這個時候B執行緒獲取到了同步鎖,在執行狀態中呼叫了 notify()方法,那麼就會將無限等待的A執行緒喚醒。注意是喚醒,如果獲取到鎖物件,那麼A執行緒喚醒後就進入 Runnable(可執行)狀態;如果沒有獲取鎖物件,那麼就進入到Blocked(鎖阻塞狀態)。
public class WaitAndSleep { public static void main(String[] args) { //建立鎖物件 Object lock = new Object(); //匿名內部類建立執行緒1 new Thread(){ @Override public void run() { System.out.println(Thread.currentThread().getName()+"需要買票"); //用同步程式碼塊包裹 synchronized (lock){ try { //lock.wait(5000);//到5秒自動醒來 lock.wait();//進入無限等待,需要喚醒 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+"買到了票"); } }.start(); //匿名內部類建立執行緒2 new Thread(){ @Override public void run() { try { Thread.sleep(5000);//等待5秒 System.out.println(Thread.currentThread().getName()+"出票了"); } catch (InterruptedException e) { e.printStackTrace(); } //用同步程式碼塊包裹 synchronized (lock){ lock.notify();//如果有多個執行緒等待,隨機喚醒一個 //lock.notifyAll();//喚醒所有等待的執行緒 } } }.start(); } }
執行緒池
當在系統中用到了很多的執行緒,大量的啟動和結束動作會導致系統的效能變卡,響應變慢,採用執行緒池可以解決這個問題。執行緒池就相當於一個容器(如同ArrayList),執行的任務放入執行緒池中,多出來的任務就等待執行緒池中的任務執行完再放入。
- 使用執行緒池的工廠類 Executors 裡的靜態方法 newFixedThreadPool 生產指定執行緒數量的執行緒池,返回為ExecutorService介面
- 建立一個類實現Runnable介面,重寫run方法,設定執行緒任務
- 呼叫ExecutorService中的submit方法,傳遞執行緒任務,開啟執行緒
- 銷燬執行緒池:ExecutorService中的shutdown方法
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; //執行緒池 public class ThreadPoolMain { public static void main(String[] args) { //使用執行緒池的工廠類 Executors裡的靜態方法 newFixedThreadPool // 生產指定執行緒數量的執行緒池,返回為ExecutorService介面 ExecutorService es = Executors.newFixedThreadPool(2); //呼叫ExecutorService中的submit方法,傳遞執行緒任務,開啟執行緒 es.submit(new ThreadPoolDemo01()); } } ////////////////////////////////////////////////////// //建立一個類實現Runnable介面,重寫run方法,設定執行緒任務 public class ThreadPoolDemo01 implements Runnable{ @Override public void run() { ... } }
以上就是JAVA多執行緒知識彙總的詳細內容,更多關於JAVA多執行緒的資料請關注我們其它相關文章!