java厨房_Java多线程基础
目錄:
進程和線程
為什么使用多線程?
多線程的創(chuàng)建方式
Runnable與Thread兩種方式比較
start()與run()方法
線程的生命周期/狀態(tài)轉(zhuǎn)換
常用方法使用與解讀
線程的優(yōu)先級
守護線程
1、進程和線程
進程(Process)是計算機中的程序關(guān)于某數(shù)據(jù)集合上的一次運行活動,是系統(tǒng)進行資源分配和調(diào)度的基本單位,是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ)。在早期面向進程設計的計算機結(jié)構(gòu)中,進程是程序的基本執(zhí)行實體;在當代面向線程設計的計算機結(jié)構(gòu)中,進程是線程的容器。程序是指令、數(shù)據(jù)及其組織形式的描述,進程是程序的實體。
一個程序就是一個進程,而一個程序中的多個任務則被稱為線程。
進程是表示資源分配的基本單位,線程是進程中執(zhí)行運算的最小單位,亦是調(diào)度運行的基本單位。
2、為什么要使用多線程?
提升效率,提升性能;發(fā)揮多核CPU的優(yōu)勢。
防止阻塞:從程序運行效率的角度來看,單核CPU不但不會發(fā)揮出多線程的優(yōu)勢,反而會因為在單核CPU上運行多線程導致線程上下文的切換,而降低程序整體的效率。但是單核CPU我們還是要應用多線程,就是為了防止阻塞。試想,如果單核CPU使用單線程,那么只要這個線程阻塞了,比方說遠程讀取某個數(shù)據(jù)吧,對端遲遲未返回又沒有設置超時時間,那么你的整個程序在數(shù)據(jù)返回回來之前就停止運行了。多線程可以防止這個問題,多條線程同時運行,哪怕一條線程的代碼執(zhí)行讀取數(shù)據(jù)阻塞,也不會影響其它任務的執(zhí)行。
3、多線程的創(chuàng)建方式[細分為5種]
繼承Thread類,重寫run方法。new一個實例。
實現(xiàn)Runnable接口,重寫run方法。作為參數(shù)傳入Thread thread = new Thread(參數(shù));來創(chuàng)建。
匿名內(nèi)部類的方式(與實現(xiàn)Runnable接口一樣,只是形式不同 )
通過并發(fā)包中Callable、Callback來創(chuàng)建
通過線程池來創(chuàng)建線程
4、Runnable與Thread兩種方式比較
繼承Thread不必多說,繼承后重寫run方法。new一個實例調(diào)用start()方法就可以了。
兩者非要比較的話,使用Runnable較好,因為實現(xiàn)接口的方式比繼承類的方式更靈活,也能減少程序之間的耦合度,面向接口編程也是設計模式6大原則的核心。
這里重點看下實現(xiàn)Runnable接口創(chuàng)建線程,實現(xiàn)Runnable接口實際上還是需要Thread類來創(chuàng)建線程,我們來看下Thread的構(gòu)造方法API:
另外需要說明的是Thread.java類也實現(xiàn)了Runnable接口,如下圖:
那也就意味著Thread的構(gòu)造函數(shù)不僅可以傳入Runnable接口的對象還可以傳入一個Thread類的對象,這樣就可以將一個Thread類的run()方法交由其他線程來調(diào)用。
5、start()與run()方法
只有調(diào)用了start()方法,才會表現(xiàn)出多線程的特性,不同線程的run()方法里面的代碼交替執(zhí)行。如果只是調(diào)用run()方法,那么代碼還是同步執(zhí)行的,必須等待一個線程的run()方法里面的代碼全部執(zhí)行完畢之后,另外一個線程才可以執(zhí)行其run()方法里面的代碼。
6、線程的生命周期/狀態(tài)轉(zhuǎn)換
生命周期的五種狀態(tài)【簡單版文字描述】:
新建(new Thread)當創(chuàng)建Thread類的一個實例(對象)時,此線程進入新建狀態(tài)(未被啟動)。
例如:Thread t1=new Thread();
就緒(runnable)線程已經(jīng)被啟動,正在等待被分配給CPU時間片,也就是說此時線程正在就緒隊列中排隊等候得到CPU資源。例如:t1.start();
運行(running)線程獲得CPU資源正在執(zhí)行任務(run()方法),此時除非此線程自動放棄CPU資源或者有優(yōu)先級更高的線程進入,線程將一直運行到結(jié)束。
死亡(dead)
當線程執(zhí)行完畢或被其它線程殺死,線程就進入死亡狀態(tài),這時線程不可能再進入就緒狀態(tài)等待執(zhí)行。
自然終止:正常運行run()方法后終止
異常終止:調(diào)用stop()方法讓一個線程終止運行,這里說明下stop()方法雖然可以停止線程,但這個方法線程不安全,在新版本的java中已經(jīng)被廢棄。
堵塞(blocked)
由于某種原因?qū)е抡谶\行的線程讓出CPU并暫停自己的執(zhí)行,即進入堵塞狀態(tài)。
正在睡眠:用sleep(long t) 方法可使線程進入睡眠方式。一個睡眠著的線程在指定的時間過去可進入就緒狀態(tài)。
正在等待:調(diào)用wait()方法。(調(diào)用notify()方法回到就緒狀態(tài))
被另一個線程所阻塞:調(diào)用suspend()方法。(調(diào)用resume()方法恢復)廢棄
更全的線程狀態(tài)轉(zhuǎn)換圖如下:
7、常用方法使用與解讀
start():使線程進入“就緒”(可運行)狀態(tài),通知jvm開啟新線程來執(zhí)行run方法。如果多次調(diào)用了start()方法,則會出現(xiàn)Exception in thread "main" java.lang.IllegalThreadStateException因為start會修改當前線程的狀態(tài)變量,只有狀態(tài)變量是初始值時才能start。
線程中斷相關(guān):
stop()【廢棄】:停止一個線程,可以使用Thread.stop()方法,但是他是不安全的,而且已經(jīng)被廢棄了呢。調(diào)用的時候還會拋出一個java.lang.ThreadDeath異常 ,但是通常情況下,此異常不需要顯示的捕捉。廢棄原因:因為強制讓線程停止則有可能使一些請理性的工作得不到完成。另外情況就是對鎖定的對象進行了“解鎖”,導致 數(shù)據(jù)得不到同步的處理,出現(xiàn)數(shù)據(jù)不一致的問題。
interrupt():大多數(shù)停止一個線程的操作是使用Thread.interrupt()方法,盡管名義為“中止,停止”但這個方法不會終止一個正在運行的線程,只是打了一個標記,還需要加入一個判斷才可以完成線程的停止。Thread.java中提供了兩個方法:
this.interrupted():測試當前線程是否已經(jīng)中斷。public static boolean interrupted()
this.isInterrupted():測試線程是否已經(jīng)中斷。public boolean isInterrupted()
具體終止線程操作(來源網(wǎng)絡):https://www.cnblogs.com/jenkov/p/juc_interrupt.html
其中的return停止線程可以的,但是還是建議使用“拋異?!钡姆椒▉韺崿F(xiàn)線程的停止,因為在catch塊中還可以將異常上拋,使線程停止的事件得以傳播。
另外這兩個方法的區(qū)別:
interrupted()無論怎么都是指正在運行的線程。而isInterrupted()就是表示指定的線程咯。
interrupted()會清除標記,什么意思呢?就是調(diào)用interrupt()給當前線程打一個中斷標記,第一次用interrupted()會返回true但是 如果不處理,之后的調(diào)用都會返回false因為它把中斷標記給清了。
暫停線程相關(guān):
suspend()與resume()【廢棄】:一個暫停線程,一個恢復線程到運行狀態(tài)。suspend()會暫停線程,假如當前線程為關(guān)鍵數(shù)據(jù)結(jié)構(gòu)加鎖 這時被掛起那么鎖將無法釋放對其他線程來說造成死鎖。同時也會因為線程的暫停出現(xiàn)數(shù)據(jù)不同步的現(xiàn)象。
currentThread():該方法返回代碼段正在被那個線程調(diào)用的信息。
isAlive():判斷當前線程是否處于活動的狀態(tài)?;顒訝顟B(tài)是指線程已經(jīng)啟動尚未終止的狀態(tài)。線程處于正在運行或準備開始運行的狀態(tài),就認為線程是“存活”的。
sleep():在指定毫秒內(nèi)讓當前正在執(zhí)行的線程休眠。這個“正在執(zhí)行的線程”是指this.currentThread()返回的線程。
getId():取得線程的唯一標識。這個是自動分配的,且是唯一的。
yield():放棄當前的CUP資源,將它讓給其他的任務去占用CPU執(zhí)行時間。但是放棄的時間不確定,有可能剛剛放棄,馬上又獲得CPU時間片。
8、線程的優(yōu)先級
線程可以劃分優(yōu)先級,CPU優(yōu)先執(zhí)行優(yōu)先級較高的線程對象中的任務。
設置線程的優(yōu)先級使用setPriority()方法,該方法的源碼如下:
/**
* Changes the priority of this thread.
*
* First the checkAccess method of this thread is called
* with no arguments. This may result in throwing a
* SecurityException.
*
* Otherwise, the priority of this thread is set to the smaller of
* the specified newPriority and the maximum permitted
* priority of the thread's thread group.
*
* @param newPriority priority to set this thread to
* @exception IllegalArgumentException If the priority is not in the
* range MIN_PRIORITY to
* MAX_PRIORITY.
* @exception SecurityException if the current thread cannot modify
* this thread.
* @see #getPriority
* @see #checkAccess()
* @see #getThreadGroup()
* @see #MAX_PRIORITY
* @see #MIN_PRIORITY
* @see ThreadGroup#getMaxPriority()
*/
public final void setPriority(int newPriority) {
ThreadGroup g;
checkAccess();
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
throw new IllegalArgumentException();
}
if((g = getThreadGroup()) != null) {
if (newPriority > g.getMaxPriority()) {
newPriority = g.getMaxPriority();
}
setPriority0(priority = newPriority);
}
}
在Java中,線程的優(yōu)先級分為【1~10】10個等級,從代碼中我們也能看到,如果小于1或者大于10,則會拋出
IllegalArgumentException異常。
JDK中使用三個常量來預定義線程的優(yōu)先級:
/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
線程優(yōu)先級的特性:
繼承性:比如A線程啟動B線程,則B線程的優(yōu)先級與A是一樣的。設置A線程的優(yōu)先級為6那么B線程也就是6。
規(guī)則性:線程優(yōu)先級等級差距很大的時候,誰先執(zhí)行完與代碼的調(diào)用順序無關(guān)。CPU盡量將資源讓給優(yōu)先級比較高的線程
隨機性:優(yōu)先級高的線程不一定每次都先執(zhí)行完。優(yōu)先級相近越能看出隨機性。
優(yōu)先級高的代碼執(zhí)行速度更快?
這是一個相對的問題,因為優(yōu)先級高的會占用更多的時間片,相同的任務量能夠更早的完成。或者說相同時間內(nèi)可以完成更多的操作。但實際上CPU處理的速度是一樣的。
9、守護線程
守護線程是一種特殊的線程,任何一個守護線程都是整個(沒錯是整個)JVM中所有非守護線程的“保姆”,只要當前JVM實例中存在任何一個非守護線程沒有結(jié)束,守護線程就在工作,只有當最后一個非守護線程結(jié)束時,守護線程才能隨著JVM一同結(jié)束工作。Daemon的作用就是為其他線程的運行提供便利服務,守護線程最經(jīng)典的應用就是GC(垃圾回收器)。
讓一個線程成為守護線程的方法是setDaemon(true);
總結(jié)
以上是生活随笔為你收集整理的java厨房_Java多线程基础的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 期中总结
- 下一篇: java打印整个向量_Java中Vect