1. 程式人生 > >Java 中的多執行緒你只要看這一篇就夠了

Java 中的多執行緒你只要看這一篇就夠了


如果對什麼是執行緒、什麼是程序仍存有疑惑,請先Google之,因為這兩個概念不在本文的範圍之內。


用多執行緒只有一個目的,那就是更好的利用cpu的資源,因為所有的多執行緒程式碼都可以用單執行緒來實現。說這個話其實只有一半對,因為反應“多角色”的程式程式碼,最起碼每個角色要給他一個執行緒吧,否則連實際場景都無法模擬,當然也沒法說能用單執行緒來實現:比如最常見的“生產者,消費者模型”。


很多人都對其中的一些概念不夠明確,如同步、併發等等,讓我們先建立一個數據字典,以免產生誤會。


  • 多執行緒:指的是這個程式(一個程序)執行時產生了不止一個執行緒

  • 並行與併發:

  • 並行:多個cpu例項或者多臺機器同時執行一段處理邏輯,是真正的同時。

  • 併發:通過cpu排程演算法,讓使用者看上去同時執行,實際上從cpu操作層面不是真正的同時。併發往往在場景中有公用的資源,那麼針對這個公用的資源往往產生瓶頸,我們會用TPS或者QPS來反應這個系統的處理能力。


執行緒安全:經常用來描繪一段程式碼。指在併發的情況之下,該程式碼經過多執行緒使用,執行緒的排程順序不影響任何結果。這個時候使用多執行緒,我們只需要關注系統的記憶體,cpu是不是夠用即可。反過來,執行緒不安全就意味著執行緒的排程順序會影響最終結果,如不加事務的轉賬程式碼:


void transferMoney

(User from, User to, float amount){
    to.setMoney(to.getBalance() + amount);
    from.setMoney(from.getBalance() - amount);
}


同步:Java中的同步指的是通過人為的控制和排程,保證共享資源的多執行緒訪問成為執行緒安全,來保證結果的準確。如上面的程式碼簡單加入@synchronized關鍵字。在保證結果準確的同時,提高效能,才是優秀的程式。執行緒安全的優先順序高於效能。


好了,讓我們開始吧。我準備分成幾部分來總結涉及到多執行緒的內容:


  1. 紮好馬步:執行緒的狀態

  2. 內功心法:每個物件都有的方法(機制)

  3. 太祖長拳:基本執行緒類

  4. 九陰真經:高階多執行緒控制類

紮好馬步:執行緒的狀態


先來兩張圖:



各種狀態一目瞭然,值得一提的是"Blocked"和"Waiting"這兩個狀態的區別:


執行緒在Running的過程中可能會遇到阻塞(Blocked)情況
對Running狀態的執行緒加同步鎖(Synchronized)使其進入(lock blocked pool ),同步鎖被釋放進入可執行狀態(Runnable)。從jdk原始碼註釋來看,blocked指的是對monitor的等待(可以參考下文的圖)即該執行緒位於等待區。


執行緒在Running的過程中可能會遇到等待(Waiting)情況
執行緒可以主動呼叫object.wait或者sleep,或者join(join內部呼叫的是sleep,所以可看成sleep的一種)進入。從jdk原始碼註釋來看,waiting是等待另一個執行緒完成某一個操作,如join等待另一個完成執行,object.wait()等待object.notify()方法執行。


Waiting狀態和Blocked狀態有點費解,我個人的理解是:Blocked其實也是一種wait,等待的是monitor,但是和Waiting狀態不一樣,舉個例子,有三個執行緒進入了同步塊,其中兩個呼叫了object.wait(),進入了waiting狀態,這時第三個呼叫了object.notifyAll(),這時候前兩個執行緒就一個轉移到了Runnable,一個轉移到了Blocked。


從下文的monitor結構圖來區別:每個 Monitor在某個時刻,只能被一個執行緒擁有,該執行緒就是 “Active Thread”,而其它執行緒都是 “Waiting Thread”,分別在兩個佇列 “ Entry Set”和 “Wait Set”裡面等候。在 “Entry Set”中等待的執行緒狀態Blocked,從jstack的dump中來看是 “Waiting for monitor entry”,而在 “Wait Set”中等待的執行緒狀態是Waiting,表現在jstack的dump中是 “in Object.wait()”。


此外,在runnable狀態的執行緒是處於被排程的執行緒,此時的排程順序是不一定的。Thread類中的yield方法可以讓一個running狀態的執行緒轉入runnable。


內功心法:每個物件都有的方法(機制)


synchronized, wait, notify 是任何物件都具有的同步工具。讓我們先來了解他們



他們是應用於同步問題的人工執行緒排程工具。講其本質,首先就要明確monitor的概念,Java中的每個物件都有一個監視器,來監測併發程式碼的重入。在非多執行緒編碼時該監視器不發揮作用,反之如果在synchronized 範圍內,監視器發揮作用。


wait/notify必須存在於synchronized塊中。並且,這三個關鍵字針對的是同一個監視器(某物件的監視器)。這意味著wait之後,其他執行緒可以進入同步塊執行。


當某程式碼並不持有監視器的使用權時(如圖中5的狀態,即脫離同步塊)去wait或notify,會丟擲java.lang.IllegalMonitorStateException。也包括在synchronized塊中去呼叫另一個物件的wait/notify,因為不同物件的監視器不同,同樣會丟擲此異常。


再講用法:


synchronized單獨使用:


程式碼塊:如下,在多執行緒環境下,synchronized塊中的方法獲取了lock例項的monitor,如果例項相同,那麼只有一個執行緒能執行該塊內容


public class Thread1 implements Runnable {
        Object lock;
        public void run() {  
            synchronized(lock){
              ..do something
            }
        }
}


直接用於方法: 相當於上面程式碼中用lock來鎖定的效果,實際獲取的是Thread1類的monitor。更進一步,如果修飾的是static方法,則鎖定該類所有例項。


public class Thread1 implements Runnable {
        public synchronized void run() {  
             ..do something
        }
}


synchronized, wait, notify結合:典型場景生產者消費者問題


/**
     * 生產者生產出來的產品交給店員
     */

    public synchronized void produce()
    {
        if(this.product >= MAX_PRODUCT)
        {
            try
            {
                wait();  
                System.out.println("產品已滿,請稍候再生產");
            }
            catch(InterruptedException e)
            {
                e.printStackTrace();
            }
            return;
        }

        this.product++;
        System.out.println("生產者生產第" + this.product + "個產品.");
        notifyAll();   //通知等待區的消費者可以取出產品了
    }

    /**
     * 消費者從店員取產品
     */

    public synchronized void consume()
    {
        if(this.product <= MIN_PRODUCT)
        {
            try 
            {
                wait(); 
                System.out.println("缺貨,稍候再取");
            } 
            catch (InterruptedException e) 
            {
                e.printStackTrace();
            }
            return;
        }

        System.out.println(

相關推薦

Java執行只要(轉)

引 如果對什麼是執行緒、什麼是程序仍存有疑惑,請先Google之,因為這兩個概念不在本文的範圍之內。 用多執行緒只有一個目的,那就是更好的利用cpu的資源,因為所有的多執行緒程式碼都可以用單執行緒來實現。說這個話其實只有一半對,因為反應“多角色”的程式程式碼,最起碼每個角色要給他一個執行緒吧,否

Java執行只要

/** * 生產者生產出來的產品交給店員 */ public synchronized void produce() { if(this.product >= MAX_PRODUCT) { try {

Java 執行只要

如果對什麼是執行緒、什麼是程序仍存有疑惑,請先Google之,因為這兩個概念不在本文的範圍之內。 用多執行緒只有一個目的,那就是更好的利用cpu的資源,因為所有的多執行緒程式碼都可以用單執行緒來實現。說這個話其實只有一半對,因為反應“多角色”的程式程式碼,最起碼每個角色要給他一個

執行(本文轉載而來) Java執行只要

Java中的多執行緒你只要看這一篇就夠了    引 如果對什麼是執行緒、什麼是程序仍存有疑惑,請先Google之,因為這兩個概念不在本文的範圍之內。 用多執行緒只有一個目的,那就是更好的利用cpu的資源,因為所有的多執行緒程式碼都可以用單執行緒來實現。說

Java線程只要

== 討論 cin 線程池。 locking nth lis dset tro 引 如果對什麽是線程、什麽是進程仍存有疑惑,請先Google之,因為這兩個概念不在本文的範圍之內。 用多線程只有一個目的,那就是更好的利用cpu的資源,因為所有的多線程代碼都可以用單線程來實現。

轉:Java線程只要

無法 線程不安全 str his ace oat 情況下 containe live 如果對什麽是線程、什麽是進程仍存有疑惑,請先Google之,因為這兩個概念不在本文的範圍之內。 用多線程只有一個目的,那就是更好的利用cpu的資源,因為所有的多線程代碼都可以用單線程來實現

Java 線程只要

並發 進入 事務 相同 人工 出了 研究 class pool 引 如果對什麽是線程、什麽是進程仍存有疑惑,請先Google之,因為這兩個概念不在本文的範圍之內。 用多線程只有一個目的,那就是更好的利用cpu的資源,因為所有的多線程代碼都可以用單線程來實現。說這個話其實只有

Java反射機制只要

今天來總結一下Java反射機制,在此之前,回顧下java程式的編譯執行過程,分為三個階段:原始碼(.java檔案)進過編譯生成位元組碼檔案(.class檔案),然後jvm載入位元組碼檔案執行程式(runtime)。 前兩個步驟(編譯階段)是在硬碟上完成的,後一個步驟(執行階段)是在記憶體中完成的

Executor執行池只

執行緒池為執行緒生命週期的開銷和資源不足問題提供瞭解決方 案。通過對多個任務重用執行緒,執行緒建立的開銷被分攤到了多個任務上。 執行緒實現方式 Thread、Runnable、Callable //實現Runnable介面的類將被Thread執行,表示一個基本任務 public interface Run

Java 的 override 和 overload,

urn 兼容性 return com 容易 定義 erl ext class 問題出現: 即使對於一個經驗豐富的開發人員來說,方法重載和方法覆蓋的區別都能讓他猶豫一下, 對於新手來說,經常容易弄混淆。 有沒有比較深入淺出的理解方式,能讓人過目不忘,用起來還能有條件反射般的

Java開發必須熟悉的Linux命令

身為一個Java開發人員,這些常用的Linux命令必須掌握。即使平時開發過程中沒有使用Linux(Unix)或者mac系統,也需要熟練掌握Linux命令。因為很多伺服器上都是Linux系統。所以,要和伺服器機器互動,就要通過shell命令、身為伺服器後端經驗豐富

容器 Java 應用程式的記憶體和 CPU 如何分配?

出品丨Docker公司(ID:docker-cn)編譯丨小東每週一、三、五,與您不見不散! 隨著2018年的結束,我們將回顧排名前五的最受讀者歡迎的文章。今天分享的第一篇文章,將幫助那些在容器中執行 Java 虛擬機器(JVM)時遇到記憶體和 CPU 大小調整/使用困難的人,本文將解釋如何在 D

效能測試:深入理解執行數,併發量,TPS,

併發數,執行緒數,吞吐量,每秒事務數(TPS)都是效能測試領域非常關鍵的資料和指標。 那麼他們之間究竟是怎樣的一個對應關係和內在聯絡? 測試時,我們經常容易將執行緒數等同於表述為併發數,這一表述正確嗎? 本文就將對效能領域的這些關鍵概念做一次探討。 文章可能會比較長,希望您保持耐心看完。   1.

python3 urllib爬蟲,只需要

寫在最前面:以下資料均脫敏 from urllib import request import requests import urllib if __name__ == "__main__": # 介面的url session_requests = requests.se

Word的表格在 Excel總是變形, 不用怕! !

我們都知道Word和Excel都能製作表格,有的時候在Word中製作好的表格想要插入到Excel中去總是會出現格式錯誤,出現表格變形。這讓我們苦惱了,辛辛苦苦做的表格又要重做了。 那麼我們如何能夠才能讓表格在Word或者Excel不出現格式上的錯誤呢? 1.其實很簡單,我們將wo

有關Java反射的使用

1. 簡介 本篇文章不探討反射的實現機制或者說實現原理,僅僅從使用的角度去講解我們常用的一些API介面,方便自己以後需要使用時信手拈來,同時也方便廣大博友能夠快速瞭解API的使用。 什麼是反射? 反射是java語言的一個特性,它允許一個java的類獲取他所有的成員變數

後臺產品的表格設計,(原型規範下載)

中後臺產品的表格設計,看這一篇就夠了(原型規範下載) 2018年4月16日luodonggan 中後臺產品的表格設計,看這一篇就夠了(原型規範下載) 經過了將近一年的後臺產品經歷,踩了很多坑,試了很多錯,也學習到了很多東西,目前也形成了自己的一套規範。本文將其中的部分收穫彙總成文,

Java NIO?

現在使用NIO的場景越來越多,很多網上的技術框架或多或少的使用NIO技術,譬如Tomcat,Jetty。學習和掌握NIO技術已經不是一個JAVA攻城獅的加分技能,而是一個必備技能。在前面2篇文章《什麼是Zero-Copy?》和《NIO相關基礎篇》中我們學習了NIO的相關理論知

Cookie介紹及在Android的使用總結超詳細,

Cookie介紹 cookie的起源 早期Web開發面臨的最大問題之一是如何管理狀態。簡言之,伺服器端沒有辦法知道兩個請求是否來自於同一個瀏覽器。那時的辦法是在請求的頁面中插入一個token,並且在下一次請求中將這個token返回(至伺服器)。這就需要在form中插入一個包含toke

Python計時,

計時對於瞭解程式的效能是很關鍵的部分。本文討論了Python 2和python 3中計時方法,並完成了一個通用的計時裝飾器。一、python2和python3的通用計時方法由於python2和3裡面的計時函式是不一樣的,建議使用timeit模組中的timeit.default