1. 程式人生 > >j2se學習中的一些零碎知識點8

j2se學習中的一些零碎知識點8

多線程概念 java程序的線程運行原理 線程的定義和創建 線程的調度與控制 線程休眠 類鎖

1、多線程的基本概念:

- 線程是指進程中的一個執行場景,也就是執行流程。(每個進程就是一個應用程序,都有獨立的內存空間;同一個進程中的線程共享其進程中的內存和資源。)

- 多進程的作用:單進程使計算機只能夠做一件事情。(如windows中的dos窗口命令行。)對於單核計算機,在同一時間點上,計算機的CPU只能夠做一件事情,由於計算機在進程和進程之間頻繁的進行切換,且切換速度極高,使人感覺多個進程在同時運行。(多進程的作用不是提高執行速度,而是為了提高CPU的使用率。)

- 需要註意,進程與進程之間的內存是獨立的。

- 線程是一個進程中的執行場景,一個進程可以啟動多個線程。多線程的作用:多線程不是為了提高執行速度,而是提高應用程序的使用率。由於計算機的執行速度非常快,可以給現實世界的人產生一種錯覺,即多個線程在同時並發運行。(需要註意,java中的線程和線程共享“堆內存和方法區內存”,棧內存是獨立的,一個線程一個棧。)


5、java程序的運行原理?

- java命令會啟動java虛擬機,啟動JVM,等於啟動了一個應用程序,表示啟動了一個進程。該進程會自動啟動一個“主線程”,然後主線程去調用某個類的main方法。所以main方法運行在主線程中。如果沒有啟動其他線程,那就只有一個主線程在運行。

技術分享


6、線程的定義、創建和啟動:(第一種方式)

- java.lang.Thread類實現了java.lang.Runnable接口,Runnable接口中聲明了run方法,Thread類中的run方法定義為:

技術分享

- run()方法在API文檔中的說明:自定義的線程構造後會調用重寫的run方法:(所以在自定義線程類時,需要繼承Thread類,並重寫Thread類中的run()方法)

技術分享




- 構造器Thread()方法創建一個線程,自定義的線程可以使用默認的構造器創建線程,默認的構造器在執行的第一行中會調用父類即Thread類的默認的構造器:

技術分享


- java.lang.Thread類中的start()方法的調用將使此線程開始執行,java虛擬機會調用此線程中的run()方法:

技術分享


- 示例代碼:(需要註意的是,有了多線程之後,main方法結束只是主線程中沒有方法棧幀,但是其他線程或者其他棧中還有棧幀,所以main方法結束,程序可能還在運行。)

技術分享


- 線程交叉運行:

技術分享


7、實現線程可以有第二種方式(自定義類實現java.lang.Runnable接口,並實現run()方法,比起自定義類直接繼承Thread類來說,這種方式比較推薦,因為這個類實現接口之外保留了類的繼承。)

- 構造器Thread(Runnable target)方法可以傳遞Runnable類型的對象創建線程:

技術分享


- 示例代碼:

技術分享


8、關於線程的生命周期:(狀態圖)

技術分享


- 解釋上例ThreadTest03示例代碼中線程生命周期的狀態:首先java命令啟動java虛擬機,java虛擬機的啟動表示開啟了一個應用程序(即進程),該進程會自動開啟一個“主線程”,然後這個主線程會調用ThreadTest03類中的main方法,在方法中使用new關鍵字創建了t線程(新建狀態),線程創建後調用start方法進入就緒狀態,這個時候主線程和t線程一起同時搶奪CPU的時間片(時間片中的時間可長可短),首先搶奪到時間片的線程由java虛擬機調度自動運行run方法,當線程中時間片中的時間段結束時另一個線程中自動運行run方法。兩個線程的時間片都結束後,兩個線程又會重新會到就緒狀態重新搶奪時間片,首先搶奪到時間片的線程繼續執行run方法(接著執行,而不是重新執行),直到run方法運行結束線程消亡。期間可能發生阻塞事件導致線程進入阻塞狀態,當阻塞解除時線程又會重新返回到就需狀態。


9、線程的調度與控制:

- 通常我們的計算即只有一個CPU,CPU在某一個時刻只能執行一條指令,線程只有得到CPU時間片,也就是使用權,才可以執行指令。在單CPU的機器上線程不是並行運行的,只有在多個CPU上線程才可以並行運行。Java虛擬機要負責線程的調度,取得CPU的使用權,目前有兩種調度模式:分時調度模型和搶占式調度模型,Java使用搶占式調度模型。

- 分時調度模型:所有線程輪流使用CPU的使用權,平均分配每個線程占用CPU的時間片。

- 搶占式調度模型:優先級高的線程獲取的CPU時間片相對會多一些。如果線程的優先級相同,那麽會隨機選擇一個。

- 線程的優先級主要分為三種:Thread類中有三個常量,MAX_PRIORITY(10、最高級)、MIN_PRIORITY(1、最低級)和NORM_PRIORITY(5、默認)。


- java.lang.Thread類中的currentThread()方法返回的是當前正在執行的線程對象:

技術分享


- java.lang.Thread類中的getName()方法返回的是線程對象的名字:(主線程默認的名字是main,其他線程的名字為Thread-編號)

技術分享


- java.lang.Thread類中的setName(String name)方法設置線程對象的名字:


技術分享


- java.lang.Thread類中可以通過getPriority()和setPriority(int newPriority)方法獲取和設置線程優先級:

技術分享

技術分享


10、關於sleep方法(線程休眠方法):Thread.sleep(毫秒),該方法的作用:阻塞當前線程,騰出CPU,讓給其他線程。(進入阻塞狀態)

技術分享


- 面試題:

技術分享


- 某線程正在休眠,如何打斷它的休眠:(第一種方式)

- java.lang.Thread類中的interrupt()方法打斷線程對象的休眠:

技術分享


- 示例代碼:

技術分享



- 中止當前線程的第二種方式:

技術分享


10、yield方法和線程合並:

- java.lang.Thread類的靜態yield()方法:會給同一個優先級的線程讓位,但是讓位時間不固定。和sleep方法相同,就是yield時間不固定。(yield是讓位的意思)

技術分享


- java.io.Thread類中的join()方法表示合並線程,等待指定線程對象死亡後再執行在線程環境中的的代碼:

技術分享


- 示例代碼:

技術分享


12、線程的同步:

- 異步編程模型和同步編程模型的區別?

有兩個線程t1和t2需要執行,t1線程執行t1,t2線程執行t2的,兩個線程之間誰也不等誰,這是異步編程模型。t1線程和t2線程執行,當t1線程必須等t2線程執行結束之後,t1線程才能執行,這是同步線程模型。

- 線程同步是為了數據安全,盡管應用程序的使用率降低,但是為了保證數據是安全的,必須加入線程同步機制。(線程同步機制使得程序變成【等同於】單線程)

- 什麽條件要使用線程同步?

1、必須是多線程環境;2、多線程環境共享同一個數據;3、共享的數據涉及到修改操作。


- 線程同步中的鎖機制:(示例代碼)

技術分享


- 以上代碼中的synchronized同步語句塊原理解析:有t1線程和t2線程,t1線程執行到此處,遇到了synchronized關鍵字,就會去找this的對象鎖,如果找到了this的對象鎖,則進入同步語句塊中執行程序,當同步語句塊中的代碼執行結束之後,t1線程歸還this的對象鎖。

在t1線程執行同步語句塊的過程中,如果t2線程也過來執行同步語句塊中的代碼,也會遇到synchronized關鍵字,所以也會去找this的對象鎖,但是該對象鎖被t1線程所持有,只能等待this對象鎖的歸還。(需要註意的是,synchronized關鍵字添加到成員方法上,線程拿到的也是this的對象鎖。)

- 需要註意的是,使用synchronized同步語句塊的方式實現線程同步控制的代碼越精細越好,因為單線程的執行程序比較消耗時間。


- 面試題:

技術分享


- 類鎖:示例代碼

技術分享


- 死鎖:示例代碼

技術分享


- 線程分類可以分為兩種:用戶線程(以上講的都是用戶線程),和守護線程。守護線程是這樣的,所有的用戶線程結束生命周期,守護線程才會結束生命周期,只要有一個用戶線程存在,那麽守護線程就不會結束。例如java中著名的垃圾回收器就是一個守護線程,只有應用程序中所有的線程結束,它才會結束。(守護線程一般是無限執行的,除非其他所有的用戶線程結束。)


- java.lang.Thread類中的setDaemon(boolean on)方法標記當前的線程是作為一個用戶線程還是守護線程。(該方法必須在線程調用start()方法之前被激活才有效。參數如果是true,則表示把當前線程設置為守護線程。)

技術分享

13、Timer定時器:java.util.Timer

- 構造器Timer()方法創建一個定時器:

技術分享


- Timer類中的schedule(TimerTask task, Date firstTime, long period)方法指定定時任務:

技術分享

- 示例代碼:

技術分享


14、Object類中的wait()方法和notify()方法:

- 導致當前線程等待,直到對象調用notify()或者是notifyAll()方法將該線程激活:

技術分享


- 喚醒單個線程,這個線程在對象監視器上等待:

技術分享


j2se學習中的一些零碎知識點8