更新時間:2018-09-14 來源:黑馬程序員 瀏覽量:
java多線程增強
java多線程基本知識
進程介紹
不管是我們開發(fā)的應(yīng)用程序,還是我們運行的其他的應(yīng)用程序,都需要先把程序安裝在本地的硬盤上。然后找到這個程序的啟動文件,啟動程序的時候,其實是電腦把當(dāng)前的這個程序加載到內(nèi)存中,在內(nèi)存中需要給當(dāng)前的程序分配一段獨立的運行空間。這片空間就專門負(fù)責(zé)當(dāng)前這個程序的運行。
不同的應(yīng)用程序運行的過程中都需要在內(nèi)存中分配自己獨立的運行空間,彼此之間不會相互的影響。我們把每個獨立應(yīng)用程序在內(nèi)存的獨立空間稱為當(dāng)前應(yīng)用程序運行的一個進程。
進程:它是內(nèi)存中的一段獨立的空間,可以負(fù)責(zé)當(dāng)前應(yīng)用程序的運行。當(dāng)前這個進程負(fù)責(zé)調(diào)度當(dāng)前程序中的所有運行細(xì)節(jié)。
1.1.2. 線程介紹
啟動的QQ聊天軟件,需要和多個人進行聊天。這時多個人之間是不能相互影響,但是它們都位于當(dāng)前QQ這個軟件運行時所分配的內(nèi)容的獨立空間中。
在一個進程中,每個獨立的功能都需要獨立的去運行,這時又需要把當(dāng)前這個進程劃分成多個運行區(qū)域,每個獨立的小區(qū)域(小單元)稱為一個線程。
線程:它是位于進程中,負(fù)責(zé)當(dāng)前進程中的某個具備獨立運行資格的空間。
進程是負(fù)責(zé)整個程序的運行,而線程是程序中具體的某個獨立功能的運行。一個進程中至少應(yīng)該有一個線程。
1.1.3. 多線程介紹
現(xiàn)在的操作系統(tǒng)基本都是多用戶,多任務(wù)的操作系統(tǒng)。每個任務(wù)就是一個進程。而在這個進程中就會有線程。
真正可以完成程序運行和功能的實現(xiàn)靠的是進程中的線程。
多線程:在一個進程中,我們同時開啟多個線程,讓多個線程同時去完成某些任務(wù)(功能)。
多線程的目的:提高程序的運行效率。
1.1.4. 多線程運行的原理
在電腦中負(fù)責(zé)程序運行的控制器CPU。
其實真正電腦中的程序的運行不是同時在運行的。CPU負(fù)責(zé)程序的運行,而CPU在運行程序的過程中某個時刻點上,它其實只能運行一個程序。而不是多個程序。而CPU它可以在多個程序之間進行高速的切換。而切換頻率和速度太快,導(dǎo)致人的肉看看不到。
每個程序就是進程, 而每個進程中會有多個線程,而CPU是在這些線程之間進行切換。
了解了CPU對一個任務(wù)的執(zhí)行過程,我們就必須知道,多線程可以提高程序的運行效率,但不能無限制的開線程。
1.1.5. Java關(guān)于線程的描述
程序運行靠的線程,在每個程序中都會一個線程的存在,線程是程序運行過程中存在一類事物,Java就必須對這個事物有類的描述和封裝。
在Java中使用Thread類描述線程這個事物。
1.1.6. 實現(xiàn)線程的兩種方式
1、繼承Thread的原理
為什么要繼承Thread類?
線程是程序運行過程中的最基本的單元。而Java對線程使用的Thread這個類進行描述。而我們現(xiàn)在希望通過自己的代碼操作線程,自己的代碼應(yīng)該需要和Thread類之間產(chǎn)生關(guān)系。這里我們采用的繼承的關(guān)系。
當(dāng)我們繼承了Thread類之后,我們自己的類也就變成了線程類。我們自己的類就繼承到了Thread類中的所有功能,并且自己的類就可以對線程進行各種操作(開啟線程,停止線程等)。
為什么要復(fù)寫run方法
為什么要使用線程:因為我們希望程序中的某段代碼可以同時運行,提高程序的運行效率。
我們定義的類繼承了Thread的之后,其實在Thread類中有個run方法,它是開啟線程之后,就會直接去運行的方法。而Java在設(shè)計線程類(Thread)的時候,就已經(jīng)明確了線程應(yīng)該執(zhí)行的某段代碼需要書寫在run方法中,之后在run方法中的代碼開啟線程之后才能正常的運行。
我們使用線程的目的是讓線程執(zhí)行后來自己程序中的某些代碼, 而Java中規(guī)定需要線程執(zhí)行的代碼必須寫run方法中,Thread類中的run方法中并沒有我們真正需要多線程運行的代碼,而開啟線程又要去運行run方法,這時我們只能沿用Thread類run方法的定義格式,然后復(fù)寫run方法的方法體代碼。
復(fù)寫run方法的目的是明確線程要執(zhí)行的代碼,只有把代碼寫在run方法中,線程開啟后才會去執(zhí)行。
需要線程執(zhí)行的代碼:這段代碼稱為線程要執(zhí)行的任務(wù)。線程要執(zhí)行的任務(wù),需要書寫在run方法中。
為什么不直接調(diào)用run方法,而調(diào)用start方法
當(dāng)書寫了一個類繼承了Thread類之后,這個子類也變成線程類。這時可以創(chuàng)建這個子類的對象,一旦創(chuàng)建Thread的子類對象,就相當(dāng)于擁有了當(dāng)前的線程對象。
創(chuàng)建Thread的子類對象,只是在內(nèi)存中有了線程這個對象,但是線程還不能真正的去運行。
要讓線程真正的在內(nèi)存運行起來,必須調(diào)用start方法,這樣才能夠在內(nèi)存開啟一片新的內(nèi)存空間,然后負(fù)責(zé)當(dāng)前線程需要執(zhí)行的任務(wù)。
我們直接通過線程對象去調(diào)用run方法,這時只是對象調(diào)用普通的方法,并沒有在內(nèi)存中開啟新的內(nèi)存空間運行任務(wù)代碼。只有調(diào)用start方法才會開啟新的空間。并在新的空間中自動去運行run方法。
2、開啟線程的第二種方式
創(chuàng)建線程的另一種方法是聲明實現(xiàn) Runnable 接口的類。該類然后實現(xiàn) run 方法。然后可以分配該類的實例,在創(chuàng)建 Thread 時作為一個參數(shù)來傳遞并啟動。
實現(xiàn)Runnable接口的原理
1、java單繼承的原因:
在Java中一個類只能有一個直接父類,如果一個類已經(jīng)繼承其他的父類,那么當(dāng)前這個類中假如有需要多線程操作的代碼,這時這類是無法再繼承Thread類的。這樣就會導(dǎo)致當(dāng)前這個類中的某些需要多線程執(zhí)行的任務(wù)代碼就無法被線程去執(zhí)行。
2、Java設(shè)計方面的原因:
Thread類是專門負(fù)責(zé)描述線程本身的。Thread類可以對線程進行各種各樣的操作。Java在設(shè)計的時候把線程要執(zhí)行的任務(wù)交給了Thread。這樣導(dǎo)致操作線程本身的功能和線程要執(zhí)行的任務(wù)功能嚴(yán)重的耦合在一起。
線程的任務(wù)是需要后來的程序制定和分配的,而線程的操作是需要提前設(shè)計好的。Java就把線程的任務(wù)從Thread類中抽取出來,保存在Runnable接口中。
把任務(wù)抽取到Runnable接口中之后,在這個接口中定義線程需要執(zhí)行的任務(wù)的規(guī)則,當(dāng)需要明確線程的任務(wù)時,我們就讓這個類實現(xiàn)Runnable接口,只要實現(xiàn)Runnable接口的類,就相當(dāng)于明確了線程需要執(zhí)行的任務(wù)。
當(dāng)一個類實現(xiàn)Runnable接口,就相當(dāng)于有了線程的任務(wù),可以是還沒有線程本身這個對象。這是我們就可以直接使用Thread這個類創(chuàng)建出線程,然后把任務(wù)交給線程。這樣就達到任務(wù)和線程的分離以及結(jié)合。
軟件設(shè)計的時候遵守原則:低耦合、高內(nèi)聚。事物和事物之間的依賴程度稱為它們的耦合度。