執行緒概念
-
行程:啟動一個應用程式就叫一個行程, 接著又啟動一個應用程式,這叫兩個行程,每個行程都有一個獨立的記憶體空間;行程也是程式的一次執行程序,是系統運行程式的基本單位;系統運行一個程式即是一個行程從創建、運行到消亡的程序,
-
執行緒:執行緒是在行程內部同時做的事情,一個行程中可以有多個執行緒,這個應用程式也可以稱之為多執行緒程式,
-
一個程式運行后至少有一個行程,一個行程中可以包含多個執行緒
-
執行緒調度:
- 分時調度:所有執行緒輪流使用 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() { ... } }
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/148109.html
標籤:Java
上一篇:Java獲取反射的三種方法
