Java基礎之多線程簡述

  首先,要辨析進程與線程的概念: java

  進程是程序執行的過程,它持有資源和線程,相對於程序自己而言具備動態性。 多線程

  線程是系統中最小的執行單元,同一個進程中可能有多個線程,它們共享該進程持有的資源。線程的通訊也稱爲線程的交互,方式主要有互斥和同步。同步是指線程之間經過共同協做完成某項工做,線程間具備次序性;互斥是指線程間對某一資源的競爭,一次只能有一個線程訪問該資源。 spa

  介紹完了這些基本概念,下面簡單介紹一下Java對多線程的支持. 線程

  java中經過類Thread和接口Runnable來實現多線程的操做。它們都有一個run方法來指定線程工做時執行的代碼。對象

Thread類的經常使用方法blog

類別繼承

名稱接口

簡介生命週期

線程的建立隊列

Thread()

 

Thread(String name)

Thread(Runnable target)

Thread(Runnable target, String name)

線程的方法

void start()

啓動線程

static void sleep(long millis)

線程休眠

static void sleep(long millis, int nanos)

void join()

某線程調用join方法後,使其餘線程等待該線程終止

void join(long millis)

void join(long millis, int nanos)

static void yield()

當前正在運行的線程馬上釋放處理器,從新加入競爭處理器的隊列

獲取線程引用

static Thread currentThread()

返回當前正在運行的線程的引用

 

  使用多線程有兩種方式,一種是直接繼承Thread類,一種是實現Runnable接口

  • 繼承Thread類,在run方法中指定須要線程執行的代碼,經過getName()和setName()方法去訪問線程名稱。經過該類對象的start方法來開啓線程。
  • 實現Runnable接口,重寫run方法來指定須要線程執行的代碼,而後把該類的對象做爲參數傳遞給Thread對象來執行。

  另外,經過volatile關鍵字聲明的成員變量能夠保證當其餘線程修改該成員變量後,本線程能夠正確讀取到此成員變量的值。

線程的狀態

  請結合OS中進程的5態來思考。如下線程狀態和方法的介紹來自DreamSea(張小哲)

  • 新生狀態(New): 當一個線程的實例被建立即便用new關鍵字和Thread類或其子類建立一個線程對象後,此時該線程處於新生(new)狀態,處於新生狀態的線程有本身的內存空間,但該線程並無運行,此時線程還不是活着的(not alive);
  • 就緒狀態(Runnable): 經過調用線程實例的start()方法來啓動線程使線程進入就緒狀態(runnable);處於就緒狀態的線程已經具有了運行條件,但尚未被分配到CPU即不必定會被當即執行,此時處於線程就緒隊列,等待系統爲其分配CPCU,等待狀態並非執行狀態; 此時線程是活着的(alive);
  • 運行狀態(Running): 一旦獲取CPU(被JVM選中),線程就進入運行(running)狀態,線程的run()方法纔開始被執行;在運行狀態的線程執行本身的run()方法中的操做,直到調用其餘的方法而終止、或者等待某種資源而阻塞、或者完成任務而死亡;若是在給定的時間片內沒有執行結束,就會被系統給換下來回到線程的等待狀態;此時線程是活着的(alive);
  • 阻塞狀態(Blocked):經過調用join()、sleep()、wait()或者資源被暫用使線程處於阻塞(blocked)狀態;處於Blocking狀態的線程仍然是活着的(alive)
  • 死亡狀態(Dead):當一個線程的run()方法運行完畢或被中斷或被異常退出,該線程到達死亡(dead)狀態。此時可能仍然存在一個該Thread的實例對象,當該Thread已經不可能在被做爲一個可被獨立執行的線程對待了,線程的獨立的call stack已經被dissolved。一旦某一線程進入Dead狀態,他就不再能進入一個獨立線程的生命週期了。對於一個處於Dead狀態的線程調用start()方法,會出現一個運行期(runtime exception)的異常;處於Dead狀態的線程不是活着的(not alive)。

 線程狀態圖

Ø線程的方法(Method)、屬性(Property)

1)優先級(priority)

每一個類都有本身的優先級,通常property用1-10的整數表示,默認優先級是5,優先級最高是10;優先級高的線程並不必定比優先級低的線程執行的機會高,只是執行的機率高;默認一個線程的優先級和建立他的線程優先級相同;

2)Thread.sleep()/sleep(long millis)

當前線程睡眠/millis的時間(millis指定睡眠時間是其最小的不執行時間,由於sleep(millis)休眠到達後,沒法保證會被JVM當即調度);sleep()是一個靜態方法(static method) ,因此他不會中止其餘的線程也處於休眠狀態;線程sleep()時不會失去擁有的對象鎖。 做用:保持對象鎖,讓出CPU,調用目的是不讓當前線程獨自霸佔該進程所獲取的CPU資源,以留必定的時間給其餘線程執行的機會;

3)Thread.yield()

  讓出CPU的使用權,給其餘線程執行機會、讓同等優先權的線程運行(但並不保證當前線程會被JVM再次調度、使該線程從新進入Running狀態),若是沒有同等優先權的線程,那麼yield()方法將不會起做用。

4)thread.join()

 使用該方法的線程會在此之間執行完畢後再往下繼續執行。

5)object.wait()  當一個線程執行到wait()方法時,他就進入到一個和該對象相關的等待池(Waiting Pool)中,同時失去了對象的機鎖—暫時的,wait後還要返還對象鎖。當前線程必須擁有當前對象的鎖,若是當前線程不是此鎖的擁有者,會拋出IllegalMonitorStateException異常,因此wait()必須在synchronized block中調用。sleep()和wait()方法的最大區別是:sleep()睡眠時,保持對象鎖,仍然佔有該鎖;而wait()睡眠時,釋放對象鎖。可是wait()和sleep()均可以經過interrupt()方法打斷線程的暫停狀態,從而使線程馬上拋出InterruptedException(但不建議使用該方法)。

6)object.notify()/notifyAll()

  喚醒在當前對象等待池中等待的第一個線程/全部線程。notify()/notifyAll()也必須擁有相同對象鎖,不然也會拋出IllegalMonitorStateException異常。

7)Synchronizing Block

 Synchronized Block/方法控制對類成員變量的訪問;Java中的每個對象都有惟一的一個內置的鎖, 每一個Synchronized Block/方法只有持有調用該方法被鎖定對象的鎖才能夠訪問,不然所屬線程阻塞;機鎖具備獨佔性、一旦被一個Thread持有,其餘的Thread就不能再擁有(不能訪問其餘同步方法),方法一旦執行,就獨佔該鎖,直到從該方法返回時纔將鎖釋放,此後被阻塞的線程方能得到該鎖,從新進入可執行狀態。

正確中止Java線程

  非正確中止的方法:stop()

  要正確中止線程,應該使用正確的退出標記。正常狀況下run方法執行完畢線程就中止了,可是有些狀況下run方法中須要while循環保持輪詢,因此應該爲while循環設置合適的退出條件,即退出標記,保證線程正確中止。

  或者使用interrupt()方法的初衷並非要中止線程,正常狀況下,調用該方法能夠將中斷標記設置爲TRUE。可是,當某個線程由於調用了wait()或者sleep()等方法而被阻塞時,此時調用interrupt()方法並不能正確的將interrupted標記設置爲TRUE,而且同時會拋出中斷異常。

 

爭用條件:當多個線程同時共享訪問同一個內存數據時,每一個線程都嘗試操做該數據,從而致使數據被破壞。

線程的互斥與同步

互斥是指在同一時刻只有一個線程能夠對臨界區進行操做。在JAVA中,能夠經過synchronized塊或者方法來實現。

在synchronized塊中,須要對某個對象進行加鎖,並將須要互斥的代碼放入synchronized塊中,從而來實現互斥行爲。得到該鎖的進程能夠進入該synchronized塊中.

而同步則是線程之間的一種通訊機制,經過同步能夠規定線程執行的順序。例如,當線程不知足訪問某資源時,能夠經過調用鎖對象的lock.wait()方法,使該線程讓出CPU,讓出鎖,並在該鎖對象的waitset中等待。知足條件時,能夠經過lock.notify()方法隨機喚醒一條線程,lock.notifyAll()則喚醒該waitset中全部的線程,使它們從新競爭該鎖。被喚醒的線程從wait()方法處繼續執行。

相關文章
相關標籤/搜索