java後端開發--線程 精簡總結(1)

線程

實現Runnable接口比繼承Thread類所具有的優勢:

1):適合多個相同的程序代碼的線程去處理同一個資源

2):可以避免java中的單繼承的限制

3):增加程序的健壯性,代碼可以被多個線程共享,代碼和數據獨立

4):線程池只能放入實現Runable或callable類線程,不能直接放入繼承Thread的類

 

    main方法其實也是一個線程。在java中所以的線程都是同時啓動的,至於什麼時候,哪個先執行,完全看誰先得到CPU的資源。

java中,每次程序運行至少啓動2個線程。一個是main線程,一個是垃圾收集線程。因爲每當使用java命令執行一個類的時候,實際上都會啓動一個JVM,每一個JVM實際就是在操作系統中啓動了一個進程

 

進程與線程的區別

線程和進程都可用於實現併發。的確,進程與線程有着千絲萬縷的關係,下面就讓我們一起來理一理:

1.線程是程序執行的最小單位,而進程是操作系統分配資源的最小單位;

2.一個進程由一個或多個線程組成,線程是一個進程中代碼的不同執行路線;

3.進程之間相互獨立,但同一進程下的各個線程之間共享程序的內存空間(包括代碼段、數據集、堆等)及一些進程級的資源(如打開文件和信號),                                     某進程內的線程在其它進程不可見;

4.調度和切換:線程上下文切換比進程上下文切換要快得多

                                

 

 

線程和進程一樣分爲五個階段:創建、就緒、運行、阻塞、終止。

    實際上所有的多線程代碼都是通過運行Thread start() 方法來運行的。因此,不管是擴展Thread類還是實現Runnable接口來實現多線程,最終還是通過Thread的對象的API來控制線程的,熟悉Thread類的API是進行多線程編程的基礎。start()方法的調用後並不是立即執行多線程代碼,而是使得該線程變爲可運行態(Runnable),什麼時候運行是由操作系統決定的。

線程的四種狀態

1、新建狀態(New):新創建了一個線程對象。

2、就緒狀態(Runnable):線程對象創建後,其他線程調用了該對象的start()方法。該狀態的線程位於可運行線程池中,變得可運行,等待獲取CPU的使用權。

3、運行狀態(Running):就緒狀態的線程獲取了CPU,執行程序代碼。

4、阻塞狀態(Blocked): 阻塞:也叫等待狀態,等待某一事件(如IO或另一個線程)執行完;阻塞狀態是線程因爲某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,纔有機會轉到運行狀態。阻塞的情況分三種:

(一)、等待阻塞:運行的線程執行wait()方法,JVM會把該線程放入等待池中。(wait會釋放持有的鎖)

(二)、同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程佔用,則JVM會把該線程放入鎖池中。

(三)、其他阻塞:運行的線程執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該線程置爲阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。(注意,sleep是不會釋放持有的鎖)

5、死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期

 


常用函數說明: ①sleep(long millis): 在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行)

                           ②join():指等待t線程終止。 即子線程結束才能運行主線程  join()的作用是:“等待該線程終止”,這裏需要理解的就是該線程是指的主線程等待子線程的終止。也就是在子線程調用了join()方法後面的代碼,只有等到子線程結束了才能執行。 在很多情況下,主線程生成並起動了子線程,如果子線程裏要進行大量的耗時的運算,主線程往往將於子線程之前結束,但是如果主線程處理完其他的事務後,需要用到子線程的處理結果,也就是主線程需要等待子線程執行完成之後再結束,這個時候就要用到join()方法了。

                         ③yield():暫停當前正在執行的線程對象,並執行其他線程。Thread.yield()方法作用是:暫停當前正在執行的線程對象,並執行其他線程。 yield()應該做的是讓當前運行線程回到可運行狀態,以允許具有相同優先級的其他線程獲得運行機會。因此,使用yield()的目的是讓相同優先級的線程之間能適當的輪轉執行。但是,實際中無法保證yield()達到讓步目的,因爲讓步的線程還有可能被線程調度程序再次選中。 yield()從未導致線程轉到等待/睡眠/阻塞狀態

                        wait() Obj.wait()與Obj.notify()必須要與synchronized(Obj)一起使用,當一個線程執行到wait()方法時,它就進入到一個和該對象相關的等待池中,同時失去(釋放)了對象的機鎖(暫時失去機鎖,wait(long timeout)超時時間到後還需要返還對象鎖);其他線程可以訪問;wait()使用notify或者notifyAlll或者指定睡眠時間來喚醒當前等待池中的線程。wiat()必須放在synchronized block中.

yield( ) --> Runnable 

sleep( ) --> Blocked

wait( )   -->等待隊列(與阻塞狀態不同)

 

wait( )和sleep( )區別

共同點: 

1. 他們都是在多線程的環境下,都可以在程序的調用處阻塞指定的毫秒數,並返回。 

2. wait()和sleep()都可以通過interrupt()方法 打斷線程的暫停狀態 ,

不同點: 

1. Thread類的方法:sleep(),yield()等 

   Object的方法:wait()和notify()等 

2. 每個對象都有一個鎖來控制同步訪問。Synchronized關鍵字可以和對象的鎖交互,來實現線程的同步。 

   sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其他線程可以使用同步控制塊或者方法。 

3. wait,notify和notifyAll只能在同步控制方法或者同步控制塊裏面使用,而sleep可以在任何地方使用 

    所以sleep()和wait()方法的最大區別是:

    sleep()睡眠時,保持對象鎖,仍然佔有該鎖;

    而wait()睡眠時,釋放對象鎖。

sleep() wait() yeild()三者之間的區別

sleep()方法會給其他線程運行的機會,而不考慮其他線程的優先級,因此會給較低線程一個運行的機會;yield()方法只會給相同優先級或者更高優先級的線程一個運行的機會。 

 2、當線程執行了sleep(long millis)方法後,將轉到阻塞狀態,參數millis指定睡眠時間;當線程執行了yield()方法後,將轉到就緒狀態。 

 3、sleep()方法聲明拋出InterruptedException異常,而yield()方法沒有聲明拋出任何異常 

 4、sleep()方法比yield()方法具有更好的移植性

 5、當線程調用了自身的sleep()方法或其他線程的join()方法,就會進入阻塞狀態(該狀態既停止當前線程,但並不釋放所佔有的資源)。當sleep()結束或join()結束後,該線程進入可運行狀態,繼續等待os分配時間片;

 6、當線程調用了yeild()方法,意思是放棄當前獲得的CPU時間片,回到可運行狀態,這時與其他進程處於同等競爭狀態,OS有可能會接着又讓這個進程進入運行狀態;

 7、當線程剛進入可運行狀態(即就緒狀態),發現將要調用的資源被synchronized(同步),獲取不到鎖標記,將會立即進入鎖池狀態,等待獲取鎖標記(這時的鎖池裏也許已經有了其他線程在等待獲取鎖標記,這時它們處於隊列狀態,即先到先得)一旦線程獲得鎖標記後,就轉入可運行狀態,等待os分配CPU時間片;

 8、當一個線程執行到wait()方法時,它就進入到一個和該對象相關的等待池中,同時失去了對象的鎖。它被一個notify()方法喚醒時,等待池中的線程就被放到鎖池中。該線程從鎖池中獲得鎖,然後回到wait()前的中斷現場。

 9.當線程調用wait()方法後會進入等待隊列(進入這個狀態會釋放所佔有的所有資源,與阻塞狀態不同),進入這個狀態後,是不能自動喚醒的,必須依靠其他線程調用notify()或notifyAll()方法才能被喚醒(wait(1000)時可以自動喚醒)(由於notify()只是喚醒一個線程。但我們由於不能確定具體喚醒的是哪一個線程,也許我們需要喚醒的線程不能夠喚醒,)因此在實際使用時,一般都用notifyAll(),方法喚醒所有線程(),當線程被喚醒後會進入鎖池,等待獲得鎖標記

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