進程
- 進程可以理解為一個正在執(zhí)行的應(yīng)用程序 QQ、瀏覽器、Web服務(wù)等
- 每一個正在執(zhí)行的應(yīng)用程序就是一個進程、 這些進程之間互不干擾,相對獨立
- 進程是一個==具有一定獨立功能的程序==,在一個數(shù)據(jù)集上的一次動態(tài)執(zhí)行的過程,是==操作系統(tǒng)進行資源分配和調(diào)度的一個獨立單位==,是應(yīng)用程序的載體
線程
- 在刷抖音的時候,可以一邊看視頻一邊進行評論;一般情況下,這時就有兩個線程,一個是播放視頻的線程,另一個是發(fā)表評論的線程;這兩個線程是同時執(zhí)行的,我們發(fā)表評論時,不影響視頻的播放,并且這兩個線程屬于同一個進程
- 線程是==操作系統(tǒng)能夠進行運算調(diào)度的最小單位==,包含在進程之中,是==進程中的實際運行單位==
- 一條線程指的是進程中一個單一順序的控制流,一個進程中可以并發(fā)(在一個時間單位中有多個人同時做事)多個線程去執(zhí)行不同的任務(wù)
進程與線程區(qū)別
- 具體完成任務(wù)的是進程所管理的線程
- 一個進程至少包含一個線程
線程的生命周期
- 新建狀態(tài) 當(dāng)使用==new關(guān)鍵字創(chuàng)建了一個線程對象時,該線程對象處于新建狀態(tài)==,它保持該狀態(tài)直到程序start()
- 就緒狀態(tài) 當(dāng)調(diào)用了==線程對象的start()方法==之后,該線程就進入就緒狀態(tài),就緒狀態(tài)下的線程處于==就緒隊列==中,要==等待JVM里線程調(diào)度器的調(diào)度==
- 運行狀態(tài) 如果就緒狀態(tài)中的線程對象獲取到了CPU資源(時間片),此時該線程進入了運行狀態(tài),開始執(zhí)行run()中的內(nèi)容;處于運行狀態(tài)的線程最為復(fù)雜,它可能進入==阻塞狀態(tài)、就緒狀態(tài)、死亡狀態(tài)==
- 阻塞狀態(tài)
- 等待阻塞 運行狀態(tài)中的線程執(zhí)行wait()方法,該線程進入等待阻塞狀態(tài)
- 同步阻塞 線程在獲取synchronized同步鎖失敗時進行同步阻塞狀態(tài)
- 其它阻塞 通過調(diào)用線程的sleep()或join(),線程也會進入阻塞狀態(tài)
- 死亡狀態(tài)
- 一個運行狀態(tài)下的線程對象==完成任務(wù)==或者==其它終止條件發(fā)生==時,該線程對象進入終止?fàn)顟B(tài)
- 阻塞狀態(tài)
線程中的常用方法
獲取當(dāng)前正在運行的線程
System.out.println(Thread.currentThread());
獲取線程名稱和ID
System.out.println(Thread.currentThread().getName());
System.out.println(Thread.currentThread().getId());
線程睡眠及中斷睡眠(喚醒)
在睡眠狀態(tài)中的線程是可以被強制喚醒的
Thread.sleep(1000);
if(!thread1.isInterrupted()) {
//中斷睡眠、喚醒
t1.interrupt();
}
線程禮讓
- ==線程的禮讓是指正在運行的線程將資源讓給別人先執(zhí)行==
- 每一次調(diào)度yield(),都只會==禮讓一次當(dāng)前的資源==,當(dāng)前正在執(zhí)行的線程對象進入就緒狀態(tài)
//線程禮讓
Thread.yield();
System.out.println(Thread.currentThread().getId() + "準(zhǔn)備睡覺了");
//讓當(dāng)前正在執(zhí)行的線程對象進入阻塞狀態(tài) 睡眠6秒 時間結(jié)束后線程對象會回到就緒狀態(tài)
Thread.sleep(6000);
System.out.println(Thread.currentThread().getId() + "睡醒了");
線程的優(yōu)先級
- 優(yōu)先級是通過int值來定義的
- ==優(yōu)先級的值從1-10,數(shù)字越大級別越高,最高10==
- 默認所有的線程優(yōu)先級都為5
- 通過設(shè)置優(yōu)先級提高當(dāng)前線程對象搶到時間片的概率
thread1.setPriority(1);
thread2.setPriority(10);
線程同步
線程安全
當(dāng)==多個線程訪問同一個資源==時,會出現(xiàn)線程安全的問題
線程安全的處理方案:==在任何一個時間點,當(dāng)處理共享數(shù)據(jù)的時候,無論當(dāng)前的線程是否在休眠,都要保證數(shù)據(jù)處理完整==
public class Test {
public static int count=5;
public static void main(String[] args) {
Thread t1 = new Thread(new Person(),"黃牛1號");
Thread t2 = new Thread(new Person(),"黃牛2號");
Thread t3 = new Thread(new Person(),"黃牛3號");
t1.start();
t2.start();
t3.start();
}
}
public class Person implements Runnable {
@Override
public void run() {
while(Test.count >= 1) {
System.out.println(Thread.currentThread().getName() + "買到了一張票,剩余票數(shù)" + --Test.count);
}
}
}
線程同步
當(dāng)我們使用多個線程訪問同一個資源的時候,為了保證每個線程都能正常執(zhí)行原子(不可再分)操作
Java引入了線程同步機制
- synchronized同步代碼塊
- synchronized同步方法
- Lock鎖
- 給共享的資源上鎖,只有擁有鑰匙的人才能進去
- synchronized
- 同步鎖
public class Person implements Runnable {
@Override
public void run() {
where(true){
synchronized(Test.count){
if(Test.count >= 1) {
System.out.println(Thread.currentThread().getName() + "買到了一張票,剩余票數(shù)" + --Test.count);
}else{
break;
}
}
}
}
}
SpringBoot的Controller
==默認是單例的==,并且在多線程環(huán)境下是線程安全的。這是因為Spring Framework通過==ThreadLocal等機制來保證單例Bean的線程安全==。
如果你的Controller中的==實例變量是線程安全的或者不可變的==,那么你不需要額外的線程安全措施。
如果你的Controller中有==實例變量是可變的==,那么你需要確保對這些可變狀態(tài)的訪問是線程安全的。
==對于單例Bean,所有線程都共享一個單例實例Bean,因此是存在資源的競爭,所以線程不安全。
如果單例Bean是一個==無狀態(tài)Bean==,也就是線程中的操作==不對Bean的成員執(zhí)行查詢以外的操作==,那么這個==單例Bean是線程安全==的。比如Spring mvc 的 Controller、Service、Dao等,這些Bean大多是無狀態(tài)的,==只關(guān)注于方法本身==,所以說在某種程度上來說Bean其實是安全的。
如果Bean是有狀態(tài)的,那就需要開發(fā)人員自己來進行線程安全的保證,最簡單的辦法就是改變bean的作用域 把 "singleton"改為“protopyte” 這樣每次請求Bean就相當(dāng)于是 new Bean() 這樣就可以保證線程的安全了
@Component
@Scope("prototype") // 將Bean的作用域設(shè)置為原型
public class MyBean {
}
- Spring默認使用單個線程模型來處理請求,這意味著一旦請求到達,Spring就會在同一個線程中處理這個請求,直到響應(yīng)被發(fā)送回客戶端。
- 單例不要使用非靜態(tài)的成員變量
- 每次請求都是獨立的線程
@RestController
public class ThreadSafeController {
// 使用原子操作保證線程安全
private final AtomicInteger counter = new AtomicInteger();
@GetMapping("/increment")
public int increment() {
return counter.incrementAndGet();
}
}
在這個例子中,==incrementAndGet()方法是原子操作,它本身是線程安全的==。AtomicInteger類的所有狀態(tài)變化都是原子的,因此不需要額外的線程安全措施。