Java線程狀態及同步鎖


線程的生命歷程

線程的五大狀態

  • 創建狀態:簡而言之,當創建線程對象的代碼出現的時候,此時線程就進入了創建狀態。這時候的線程只是行代碼而已。只有調用線程的start()方法時,線程的狀態才會改變,進入就緒狀態
  • 就緒狀態:在這個狀態下的線程,已經做好了隨時運行的准備,但是並不意味着會立刻開始運行。還需要等待CPU的隨機調度,隨機運行只有當線程被CPU調度運行成功,此時的線程才算是進入下一個狀態——運行狀態。
  • 運行狀態:線程處於運行狀態,主要是在運行線程中的代碼塊。
  • 阻塞狀態:在線程運行過程中,當線程代碼塊中調用了線程的sleep(),yield(),同步鎖定或者其他使線程阻塞的方法,此時的線程無法繼續運行下去,進入了阻塞狀態(線程代碼塊的自身邏輯混亂也可以使線程阻塞)。當造成線程阻塞的阻塞事件解決之后,線程不會回到運行狀態,而是回到就緒狀態,再次等待CPU的調度運行。需要注意的是,阻塞並不意味着線程運行終止
  • 死亡狀態當線程成功運行完所有的代碼之后,線程就結束了,也進入了死亡狀態。線程一旦死亡,就無法再次啟動,注意這里和阻塞狀態的不同。同樣的,當線程運行一半的時候被強行結束終止,也算進入死亡狀態,也無法被再次啟動。

線程的方法

Java中的thread類自帶有線程的一些方法,這些方法可以讓線程睡眠,插隊,提高線程調度的優先級等等,它們提供了改變線程狀態的操作手段。(不過在JDK幫助文檔中,一些方法已經不推薦使用)

線程方法中的一些有趣的地方

  • 線程睡眠是以毫秒為單位的。一秒等於一千毫秒。一般在測試程序中調用睡眠方法,是為了提高程序問題的發生性,或者說為了發現bug

  • 線程停止,由於Java中自帶的停止方法不太好用,所以一般都是自己寫一個停止的方法,標定一個布爾類型的Flag作為線程執行的標志,當flag為真時線程運行,當flag為假時線程停止。

  • 線程禮讓是將正在運行的線程暫停回到就緒狀態,而不是變為阻塞狀態。有趣的是禮讓不是一定會成功的,因為線程由就緒狀態進入運行狀態是由CPU隨機調度的。所以禮讓的線程有可能在下次的調度中再次提前調度,提前運行。

  • 線程插隊(join方法),強制阻塞其他線程,只有插入的線程執行完成之后,其他線程才能繼續執行

  • 線程雖然有優先級的區別(1-10),但是在實際運行中還是得看CPU的心情調度運行,優先級高只是被調度的概率高一點。Java中自帶有線程優先級的查看和改變方法(線程的優先級設置最好在線程啟動之前)

    public class ttp {
        public static void main(String[] args) {
            //主線程的默認優先級
            System.out.println(Thread.currentThread().getName()+"--->" + Thread.currentThread().getPriority());
            MyPriorty mm = new MyPriorty();
            Thread t1 = new Thread(mm);
            Thread t2 = new Thread(mm);
            Thread t3 = new Thread(mm);
    
    
            
            t1.setPriority(10);
            t1.start();
    
            t2.setPriority(4);
            t2.start();
    
            t3.setPriority(6);
            t3.start();
    
    
        }
    }
    
    //Runnable接口實現接口,run方法為打印線程名稱和線程的優先級
    class MyPriorty implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"--->" + Thread.currentThread().getPriority());
        }
    }
    //這里的輸出有多種結果,因為優先級只是增加了線程被調度運行的機率
    
  • 用戶線程和守護線程。守護線程的作用是保證用戶線程的執行過程正常,例如Java中的內存回收線程和后台記錄操作日志等等,這些都是守護線程。虛擬機必須等待用戶線程執行完畢,不用等待守護線程執行完畢。當用戶線程完成后,虛擬機就自動關閉,守護線程也就自動死亡了。

    //Java的Thread類自帶設置守護線程的方法
    Thread.setdaemon(true) //設置為守護線程
    //一般我們創建的線程默認都是用戶線程
    
  • 線程同步。線程同步是出現多個線程訪問同一個對象並都想對其進行操作的時候必須考慮的問題。不進行線程同步(並發)控制的多線程是不安全的。

    
    //線程不安全,出現了-1張票以及有兩個線程拿到同一張票的錯誤,所以這是一個不安全的線程
    public class test05 {
    
        public static void main(String[] args) {
            buyTicket b1 = new buyTicket();
            Thread t1 = new Thread(b1,"you");
            Thread t2 = new Thread(b1,"i");
            Thread t3 = new Thread(b1,"he");
            Thread t4 = new Thread(b1,"she");
            t1.start();
            t2.start();
            t3.start();
            t4.start();
    
        }
    }
    
    
    class buyTicket implements Runnable{
        //剩余票數
        private int ticketNums = 12;
        private boolean flag = true;
    
        @Override
        public void run() {
            while(flag){
                buy();
            }
        }
    
        private void buy(){
            if(ticketNums <= 0){
                flag = false;
                return;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "買到了第" + ticketNums-- + "張票");
        }
    }
    

線程同步

線程同步實質上是一個等待機制。線程同步時會將多個線程放入對象等待池中進行排隊(隊列),等待前一個線程執行操作完畢,再有下一個線程進行執行操作。每個對象都有一個獨有的鎖(排他鎖),每個線程執行時都會獲得對象的排他鎖,這樣只有獲得鎖的線程可以對對象進行操作,執行結束后排他鎖被下一個線程獲得。總結來說,線程同步的形成條件就是:隊列+鎖

在訪問時加入鎖機制synchronized,當一個線程獲得對象得排他鎖,獨占資源,其他線程必須等待,使用后釋放鎖即可

線程同步也有一些存在的問題(大部分是以犧牲性能以保證安全)

  • 一個線程持有鎖會導致其他所有需要此鎖的線程掛起
  • 在多線程競爭下,加鎖,釋放鎖會導致較多的上下文切換和調度延時,引起性能問題
  • 如果一個優先級高的線程等待一個優先級低的線程釋放鎖,會導致優先級倒置,引起性能問題

一般來說,synchronized是方法聲明中添加,默認對方法中的this對象資源添加鎖。如果要對其他共享資源對象進行鎖定,則要使用同步監視器

  • 一號線程訪問,鎖定監視器,開始執行中間的代碼
  • 二號線程訪問,發現監視器被鎖,無法訪問,掛起
  • 一號線程執行完畢,解鎖監視器
  • 二號線程訪問,監視器無鎖,鎖定並執行代碼
public void xxx(){
    //其中ob就是想要鎖住的任意的共享資源對象
    //代碼塊是放在同步監視器中的
    synchronized(obj){
        ....
    }
}

需要注意的是,這樣的鎖理論上是可行的 ,但是在實際運行中雖然加了鎖,但是還是有可能出現不安全的現象

"本站所有文章均為原創,歡迎轉載,請注明文章出處:Thales_ZeeWay百度和各類采集站皆不可信,搜索請謹慎鑒別。技術類文章一般都有時效性,本人習慣不定期對自己的博文進行修正和更新,因此請訪問出處以查看本文的最新版本。"

作者:Thales_ZeeWay
鏈接:Java線程狀態及同步鎖
來源:博客園

著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM