雲端衛士實戰錄 | Java高級特性之多線程


《實戰錄》導語

一轉眼作爲一名Java開發者已經四年多時間了,說長不長說短不短,對於java的感情還是比較深的,主要嘛畢竟它給了我飯喫。哈哈,開個玩笑。今天我想借此機會來和大家聊聊Java多線程。文中若有錯誤還請各位小夥伴及時指出。


Java是一種跨平臺,適合於分佈式計算環境的面向對象編程語言。關於Java的優點,我想每個剛接觸Java的朋友,都會聽過你的Java啓蒙老師說過這麼一句話:“一次編譯,到處運行”。這個“到處運行”是說任何平臺上只要安裝了JRE,就可以運行已經編譯過的(不管是什麼環境編譯的)Java程序。


​既然Java 這麼厲害,那麼想要知道爲什麼厲害肯定是要知道他的優點嘛。那麼我們就去體驗下Java的高級特性。Java主要高級特性有1.線程 2.IO 3.類集 4.反射(排名不分先後)等等。今天我想借此機會來和大家聊聊Java多線程。文中若有錯誤還請各位小夥伴及時指出。


瞭解一樣東西,我時常會關心的問題基本和劉姥姥進大觀園一樣。二點 1.它是什麼?(多線程是啥?) 2.它可以做什麼?(多線程在什麼樣的場景下使用?怎麼去用?)


它是什麼?


1.1進程和線程的概念


進程:運行中的應用程序稱爲進程,擁有系統資源(cpu、內存)。


線程:進程中的一段代碼,一個進程中可以有多段代碼。本身不擁有資源(共享所在進程的資源)。舉個例子:假如我們都是工地搬磚工,我們每天都要不停的把磚搬上卡車(排隊依次放磚),我們排隊放磚(循環的排隊,一直排下去,直到我不想幹了離職,退出),每個搬磚工都有機會到達隊伍的最前端把磚頭放入卡車,這個就好比是線程,都有機會被程序執行。但是線程真正起作用的時候,就是我們在隊伍的把磚頭放入卡車,這一段時間,這是線程真正執行的階段。


在java中,程序入口被自動創建爲主線程,在主線程中可以創建多個子線程。

兩者的區別

 1、是否佔有資源問題。
 2、創建或撤銷一個進程所需要的開銷比創建或撤銷一個線程所需要的開銷大。
 3、進程爲重量級組件,線程爲輕量級組件

多進程: 在操作系統中能同時運行多個任務(程序)。
多線程: 在同一應用程序中有多個功能流同時執行。


1.2線程的主要特點


①、不能以一個文件名的方式獨立存在在磁盤中;
②、不能單獨執行,只有在進程啓動後纔可啓動;
③、線程可以共享進程相同的內存(代碼與數據)。既然可以共享相同內存,這邊就會有個數據一致性的問題。


1.3 線程的主要用途


①、利用它可以完成重複性的工作(如每天鬧鐘時間的提醒)。
②、從事一次性較費時的初始化工作(如網絡連接、Web服務中加載一些菜單權限等)。
③、併發執行的運行效果(一個進程多個線程)以實現更復雜的功能


1.4、多線程(多個線程同時運行)程序的主要優點


①、可以減輕系統性能方面的瓶頸,因爲可以並行操作;
②、提高CPU的處理器的效率,在多線程中,通過優先級管理,可以使重要的程序優先操作,提高了任務管理的靈活性;另一方面,在多CPU系統中,可以把不同的線程在不同的CPU中執行,真正做到同時處理多任務。


以上說明了線程的基本概念。那麼接下來就用代碼來說明幾個小例子看下多線程在什麼樣的場景下使用?怎麼去用?


它可以做什麼?


java中創建一個線程有兩種方式:


1、繼承Thread類,重寫run()方法,然後直接new這個對象的實例,創建一個線程的實例。然後調用start()方法啓動線程。
2、實現Runnable接口,重寫run()方法,然後調用new Thread(runnable)的方式創建一個線程,然後調用start()方法啓動線程。


對於這兩種方式的區別是:


繼承Thread:線程代碼存放Thread子類run方法中。
實現Runnable,線程代碼存在接口的子類的run方法。
實現Runnable接口相對於繼承Thread類來說,有如下的顯著優勢:


1.適合多個相同代碼的線程去處理同一個資源的情況
2.可以避免由於java的單繼承特性帶來的侷限
3.增強了程序的健壯性,代碼能夠被多個線程共享,代碼與數據時獨立的


看個有趣的例子,對比如下:


場景:某建築工地包工頭新進一批磚頭有15W塊,要從A地點搬到B地點。假設工地上有三個工人(看做線程哦),每天工人推板車拉磚的次數是5次,每次一車假設可以拉1W。包工頭很人性化平均分道每個人頭上5W磚。我們來看下非常有趣的Java代碼。 


A.繼承Thread方式Code:


//新建一個搬磚仔的線程
public class BanZhuanThread extends Thread {
    //工人名字
    private String name;


    public BanZhuanThread(String name)
    {
        this.name = name;
    }
    //磚頭塊數5W 老闆分的很均勻,人很好!
    private int bricks = 5;
    @Override
    public void run()
    {
        for(int i=0 ;i<5;i++)
        {
            System.out.println(name + "正在搬磚!  剩餘磚頭 " + bricks-- +"萬");
            if(i==4)
            {
                System.out.println("我是"+name+",活幹完了,下班");
            }
        }
    }
}


//新建三個工人去搬磚,分別新建三個工人線程大強,小強,中強去搬磚


public class TestThread {
    public static void main(String[] args)
    {
        Thread catThread = new BanZhuanThread("小強");
        catThread.start();


        Thread catThread2 = new BanZhuanThread("大強");
        catThread2.start();


        Thread catThread3 = new BanZhuanThread("中強");
        catThread3.start();


    }
}


//結果輸出
大強正在搬磚!  剩餘磚頭 5萬
大強正在搬磚!  剩餘磚頭 4萬
大強正在搬磚!  剩餘磚頭 3萬
大強正在搬磚!  剩餘磚頭 2萬
大強正在搬磚!  剩餘磚頭 1萬
我是大強,活幹完了,下班
中強正在搬磚!  剩餘磚頭 5萬
中強正在搬磚!  剩餘磚頭 4萬
中強正在搬磚!  剩餘磚頭 3萬
中強正在搬磚!  剩餘磚頭 2萬
中強正在搬磚!  剩餘磚頭 1萬
我是中強,活幹完了,下班
小強正在搬磚!  剩餘磚頭 5萬
小強正在搬磚!  剩餘磚頭 4萬
小強正在搬磚!  剩餘磚頭 3萬
小強正在搬磚!  剩餘磚頭 2萬
小強正在搬磚!  剩餘磚頭 1萬
我是小強,活幹完了,下班


B.實現Runnable接口方式Code:


//新建一個搬磚仔的線程
public class BanZhuanRunnable implements Runnable {



    //磚頭總塊數15W !
    private int bricks = 15;


    @Override
    public void run() {
        for(int i=0 ;i<5;i++)
        {
            System.out.println(Thread.currentThread().getName() + "正在搬磚!  剩餘磚頭 " + bricks--  +"萬");
            if(i==4)
            {
                System.out.println("我是"+Thread.currentThread().getName()+",活幹完了,下班");
            }
        }
    }
}


//新建三個工人去搬磚,分別新建三個工人線程大強,小強,中強去搬磚
public class TestThread {
    public static void main(String[] args)
    {


        Runnable catThread = new BanZhuanRunnable();
        new Thread(catThread,"小強").start();
        new Thread(catThread,"大強").start();
        new Thread(catThread,"中強").start();
    }
}


//結果輸出
小強正在搬磚!  剩餘磚頭 14萬
中強正在搬磚!  剩餘磚頭 13萬
中強正在搬磚!  剩餘磚頭 11萬
中強正在搬磚!  剩餘磚頭 10萬
中強正在搬磚!  剩餘磚頭 9萬
中強正在搬磚!  剩餘磚頭 8萬
我是中強,活幹完了,下班
大強正在搬磚!  剩餘磚頭 15萬
小強正在搬磚!  剩餘磚頭 12萬
小強正在搬磚!  剩餘磚頭 6萬
小強正在搬磚!  剩餘磚頭 5萬
小強正在搬磚!  剩餘磚頭 4萬
我是小強,活幹完了,下班
大強正在搬磚!  剩餘磚頭 7萬
大強正在搬磚!  剩餘磚頭 3萬
大強正在搬磚!  剩餘磚頭 2萬
大強正在搬磚!  剩餘磚頭 1萬
我是大強,活幹完了,下班


看完以上代碼我們可以發現在實現Runnable接口方式Code中驗證了適合多個相同代碼的線程去處理同一個資源(磚塊)的情況。而不是像繼承Thread方式Code中那樣先分割。


PS:既然是共享的資源變量,那麼如Runnable接口方式Code中的private int bricks = 15;就會有線程安全問題。所以如果在數據量大的情況Runnable接口方式Code代碼是有一定問題的。但是從中已經可以看出他們之間的區別。如果看完還是沒感覺自己敲一把體驗下那個包工頭的例子。


總結


由於篇幅有限,上面只是簡單介紹了下Java中怎麼創建線程以及基本的使用,但是已經可以體會到多線程的樂趣所在。上述列子也反映出了多線程過程中對共享資源的線程安全問題。解決這種問題就會引出鎖的感念,以及多線程下怎麼性能的優化。


最後說下本人對於多線程使用場景的一些經驗。


1.如果你的一個大任務可以分成多個獨立的並且互不影響的並且重複度極高子任務的話。那麼大膽用多線程去做吧。控制好內存中線程數等問題。
2.特別耗時的操作,如備份數據庫,可以開個線程執行備份,然後執行返回,前臺不斷向後臺詢問線程執行狀。
3.需要異步處理的時候,需要使用多線程。
4.對於多線程中,執行失敗的時候儘可能要有補償機制。哪怕是寫日誌記錄也可以。


說着說着就想不到了。。。。。先這樣吧。-.-!



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章