國內首例!成渝客專高鐵將提速至350公里每小時
概念
程式碼:即文字檔案,是靜態的。
程式:程式碼在記憶體中執行,是動態的。
程序與執行緒:
- 一個程式代表一個程序,一臺主機有多個程序,一個程序有多個執行緒
- 程序是系統進行資源分配的基本單位,執行緒是進行運算排程的最小單位。
- 程序是執行緒的容器,執行緒是程序中的實際運作單位
執行緒狀態
建立狀態:執行緒物件一旦建立就進入到新生狀態
就緒狀態:呼叫start方法後,執行緒立即進入就緒狀態,但不意味著立即排程執行
執行狀態:由cpu分配到資源,才進入執行狀態,真正執行執行緒的程式碼塊
阻塞狀態:當呼叫sleep、wait或同步鎖定時,執行緒進入阻塞狀態。此時,程式碼不往下執行,阻塞事件解除後,重新進入就緒狀態,等待cpu排程執行
死亡狀態:執行緒中斷或結束,一旦進入死亡狀態,就不能再次啟動
建立執行緒的方法
1)繼承Thread類
- 自定義執行緒類繼承Thread類
- 重寫run方法,編寫執行緒執行體
- 建立執行緒物件,呼叫start方法啟動執行緒
2)實現Runnable介面
- 自定義執行緒類實現Runnable介面
- 重寫run方法,編寫執行緒執行體
- 建立執行緒物件,呼叫start方法啟動執行緒
3)通過Callable和Future建立
- 實現Callable介面,需要返回值型別
- 重寫call方法,需要丟擲異常
- 建立目標物件
- 建立執行服務
- 提交執行
- 獲取結果
- 關閉服務
常用API
執行緒api
//更改執行緒的優先順序 setPriority(int new Priority) //在指定的毫秒數內讓當前正在執行的執行緒休眠 static void sleep(long millis) //等待該執行緒終止 void join() //暫停當前正在執行的執行緒物件,並執行其它執行緒 static void yield() //中斷執行緒,別用該方式 void interrupt() //測試執行緒是否處於活動狀態 boolean isAlive()
執行緒停止
不推薦使用JDK提供的stop()、destroy()方法。已廢棄。
推薦執行緒自己停止下來——>利用次數,不建議死迴圈,即使死迴圈也應該新增延時。
建議使用一個標誌位進行終止變數。當flag=false時,終止執行緒執行。
private boolean flag=true;
public void(){
while(flag){}
}
public void stop(){
this.flag=false;
}
執行緒休眠
- sleep(時間)指定當前執行緒阻塞的毫秒數
- sleep存在異常InterruptException
- sleep時間達到後執行緒進入就緒狀態
- sleep可以模擬網路延時,倒計時等
- 每一個物件都有一個鎖,sleep不會釋放鎖
//每秒輸出系統時間
public class SleepDemo {
public static void main(String[] args) {
Date date = new Date(System.currentTimeMillis());
while(true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
date = new Date(System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
執行緒禮讓
- 禮讓執行緒,讓當前正在執行的執行緒暫停,但不阻塞
- 將執行緒從執行狀態轉為就緒狀態
- 讓cpu重新排程,禮讓不一定成功!看cpu心情
public class YieldDemo {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"執行緒開始執行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"執行緒停止執行");
}
}
執行緒強制執行
- Join合併執行緒,待此執行緒執行完成後,再執行其它執行緒,其它執行緒阻塞
public class JoinDemo implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println("執行緒vip來了"+i);
}
}
public static void main(String[] args) throws InterruptedException {
JoinDemo joinDemo = new JoinDemo();
Thread thread = new Thread(joinDemo);
thread.start();
for(int i=0;i<1000;i++){
if(i==200){
thread.join();
}
System.out.println("main--"+i);
}
}
}
執行緒狀態觀測
NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED
public class StateDemo{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for(int i=0;i<5;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//觀察狀態
Thread.State state = thread.getState();
System.out.println(state);//NEW
//觀察啟動後
thread.start();
state = thread.getState();
System.out.println(state);
while(state!=Thread.State.TERMINATED){
Thread.sleep(100);
state = thread.getState();
System.out.println(state);//輸出狀態
}
}
}
執行緒優先順序
- Java提供一個執行緒排程器來監控程式中啟動後進入就緒狀態的所有執行緒,執行緒排程器按照優先順序決定應該排程哪個執行緒來執行
- 執行緒的優先順序用數字表示,範圍從1-10
- Thread.MIN_PRIORITY=1
- Thread.MAX_PRIORITY=10
- Thread.NORM_PRIORITY=5
- 使用以下方式改變或獲取優先順序
- getPriority(),setPriority(int x)
先設定優先順序,再啟動
優先順序低只是意味著獲得排程的概率低,並不是優先順序低就不會被呼叫了,這都是看cpu的排程
守護執行緒
- 執行緒分為使用者執行緒和守護daemon執行緒
- 虛擬機器必須確保使用者執行緒執行完畢
- 虛擬機器不用等待守護執行緒執行完畢,如後臺記錄操作日誌、監控記憶體,垃圾回收等
//守護執行緒
public class DaemonDemo {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread thread = new Thread(god);
thread.setDaemon(true);//預設是false,表示是使用者執行緒
thread.start();//守護執行緒啟動
new Thread(you).start();
}
}
//上帝
class God implements Runnable{
@Override
public void run() {
while(true){
System.out.println("上帝保護著你");
}
}
}
//個人
class You implements Runnable{
@Override
public void run() {
for(int i=0;i<36500;i++){
System.out.println("你,開心的活著"+i);
}
System.out.println("good bye!world");
}
}
執行緒同步機制
併發:同一個物件被多個執行緒同時操作
搶票事件&取錢事件&廁所
處理多執行緒問題時,多個執行緒訪問同一個物件,並且某些執行緒還想修改這個物件。這時候我們就需要執行緒同步。執行緒同步其實就是等待機制,多個需要同時訪問此物件的執行緒進入這個物件的等待池形成佇列,等待前面執行緒使用完畢,下一個執行緒再使用
佇列和鎖
由於同一程序的多個執行緒共享同一塊儲存空間,在帶來方便的同時,也帶來了訪問衝突問題,為了保證資料在方法中被訪問時的正確性,在訪問時加入鎖機制synchronized,當一個執行緒獲得物件的排它鎖,獨佔資源,其它執行緒必須等待。
使用後釋放鎖即可,存在以下問題:
- 一個執行緒持有鎖會導致其它所有需要此鎖的執行緒掛起
- 在多執行緒競爭下,加鎖、釋放鎖會導致比較多的上下文切換和排程延時,引起效能問題
- 如果一個優先順序高的執行緒等待一個優先順序低的執行緒釋放鎖會導致優先順序倒置,引起效能問題
同步方法
由於我們可以通過private關鍵字來保證資料物件只能被方法訪問,所以只需 要對方法提出一套機制,這套機制就是synchronized關鍵字,它包括兩種方法:synchronized方法和synchronized塊
public synchronized void method(){}
synchronized方法控制對“物件”的訪問,每個物件對應一把鎖,每個synchronized方法都必須獲取呼叫該方法的物件的鎖才能執行,否則執行緒會阻塞,方法一旦執行,就獨佔該鎖,直到該方法返回才釋放鎖,後面被阻塞的執行緒才能獲得這個鎖,繼續執行
缺陷:若將一個大的方法宣告為synchronized將會影響效率
同步塊
synchronized(Ojb){}
Obj稱之為同步監視器
- Obj可以是任何物件,但是推薦使用共享資源作為同步監視器
- 同步方法無需指定同步監視器,因為同步方法的同步監視器就是this,就是這個物件本身,或者是class
同步監視器的執行過程
- 第一個執行緒訪問,鎖定同步監視器,執行其中程式碼
- 第二個執行緒訪問,發現同步監視器被鎖定,無法訪問
- 第一個執行緒訪問完畢,解鎖同步監視器
- 第二個執行緒訪問,發現同步監視器沒有鎖,然後鎖定並訪問
鎖的是需要變化的量
死鎖
多個執行緒各自佔有一些共享資源,並且互相等待其它執行緒佔有的資源才能執行,而導致兩個或者多個執行緒都在等待對方釋放資源,都停止執行的情形。
某一個同步塊同時擁有兩個以上物件的鎖時,就可能會發生死鎖的問題
產生死鎖的四個必要條件:
- 互斥條件:一個資源每次只能被一個執行緒使用
- 請求與保持條件:一個執行緒因請求資源而阻塞時,對已獲得的資源保持不放
- 不剝奪條件:執行緒已獲得的資源,在未使用完之前,不能強行剝奪
- 迴圈等待條件:若干執行緒之間形成一種頭尾相接的迴圈等待資源關係
只要想辦法破壞其中的任意一個或多個條件,就可以避免死鎖發生
Lock鎖
- 從JDK5.0開始,Java提供了更強大的執行緒同步機制——通過顯式定義同步鎖物件來實現同步,同步鎖使用Lock物件充當
- java.util.concurrent.locks.Lock介面是控制多個執行緒對共享資源進行訪問的工具。鎖提供了對共享資源的獨佔訪問,每次只能有一個執行緒對Lock物件加鎖,執行緒開始訪問共享資源之前應先獲得Lock物件
- ReentrantLock類實現了Lock,它擁有與synchronized相同的併發性和記憶體語義,在實現執行緒安全的控制中,比較常用的是ReentrantLock,可以顯式加鎖、釋放鎖