線程與進程

進程

  • 進程可以理解為一個正在執(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ù)的是進程所管理的線程
  • 一個進程至少包含一個線程

線程的生命周期

  1. 新建狀態(tài) 當(dāng)使用==new關(guān)鍵字創(chuàng)建了一個線程對象時,該線程對象處于新建狀態(tài)==,它保持該狀態(tài)直到程序start()
  2. 就緒狀態(tài) 當(dāng)調(diào)用了==線程對象的start()方法==之后,該線程就進入就緒狀態(tài),就緒狀態(tài)下的線程處于==就緒隊列==中,要==等待JVM里線程調(diào)度器的調(diào)度==
  3. 運行狀態(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)

線程中的常用方法

獲取當(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)變化都是原子的,因此不需要額外的線程安全措施。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容