多線程:
1、程序和線程:
程序:正在進行的程式。每一個程序執行都有一個執行順序,該順序是一個執行路徑,或者叫一個控制單元。
線程:程序内部的一條執行路徑或者一個控制單元。
兩者的差別:
一個程序至少有一個線程
程序在執行過程中擁有獨立的記憶體單元,而多個線程共享記憶體;
2、jvm多線程的啟動是多線程嗎?
java的虛拟機jvm啟動的是單線程,就有發生記憶體洩露的可能,而我們使用java程式沒出現這樣的問題,
也就是jvm啟動至少有兩個線程,一個執行java程式,一個執行垃圾回收。是以是多線程。
2、多線程的優勢:
解決了多部分同時運作的問題,提高效率
3、線程的弊端:
線程太多會導緻效率的降低,因為線程的執行依靠的是CPU的來回切換。
4、什麼叫多線程:
一個程序中有多個線程,稱為多線程。
5、實作多線程的方法:
實作多線程可以通過繼承Thread類和實作Runnable接口。
(1)繼承Thread
定義一個類繼承Thread類
複寫Thread類中的public void run()方法,将線程的任務代碼封裝到run方法中
直接建立Thread的子類對象,建立線程
調用start()方法,開啟線程(調用線程的任務run方法)
//另外可以通過Thread的getName()擷取線程的名稱。
(2)實作Runnable接口;
定義一個類,實作Runnable接口;
覆寫接口的public void run()的方法,将線程的任務代碼封裝到run方法中;
建立Runnable接口的子類對象
将Runnabl接口的子類對象作為參數傳遞給Thread類的構造函數,建立Thread類對象
(原因:線程的任務都封裝在Runnable接口子類對象的run方法中。
是以要線上程對象建立時就必須明确要運作的任務)。
調用start()方法,啟動線程。
兩種方法差別:
(1)實作Runnable接口避免了單繼承的局限性
(2)繼承Thread類線程代碼存放在Thread子類的run方法中
實作Runnable接口線程代碼存放在接口的子類的run方法中;
在定義線程時,建議使用實作Runnable接口,因為幾乎所有多線程都可以使用這種方式實作
6、建立線程是為什麼要複寫run方法?
Thread類用于描述線程。Thread類定義了一個功能,用于存儲線程要運作的代碼,該存儲功能就是run方法。
7、start()和run方法有什麼差別?
調用start方法方可啟動線程,而run方法隻是thread的一個普通方法,調用run方法不能實作多線程;
Start()方法:
start方法用來啟動線程,實作了多線程運作,這時無需等待run方法體代碼執行完畢而直接繼續執行下面的
代碼。通過調用Thread類的start()方法來啟動一個線程,這時此線程處于就緒(可運作)狀态,并沒有運作,
一旦得到cpu時間片(執行權),就開始執行run()方法,這裡方法run()稱為線程體,
它包含了要執行的這個線程的内容,Run方法運作結束,此線程随即終止。
Run()方法:
run()方法隻是Thread類的一個普通方法,如果直接調用Run方法,程式中依然隻有主線程這一個線程,
其程式執行路徑還是隻有一條,還是要等待run方法體執行完畢後才可繼續執行下面的代碼,
這樣就沒有達到多線程的目的。
8、線程的幾種狀态:
建立:new一個Thread對象或者其子類對象就是建立一個線程,當一個線程對象被建立,但是沒有開啟,這個時候,
隻是對象線程對象開辟了記憶體空間和初始化資料。
就緒:建立的對象調用start方法,就開啟了線程,線程就到了就緒狀态。
在這個狀态的線程對象,具有執行資格,沒有執行權。
運作:當線程對象擷取到了CPU的資源。
在這個狀态的線程對象,既有執行資格,也有執行權。
當機:運作過程中的線程由于某些原因(比如wait,sleep),釋放了執行資格和執行權。
當然,他們可以回到運作狀态。隻不過,不是直接回到。
而是先回到就緒狀态。
死亡:當線程對象調用的run方法結束,或者直接調用stop方法,就讓線程對象死亡,在記憶體中變成了垃圾。
9、sleep()和wait()的差別:
(1)這兩個方法來自不同的類,sleep()來自Thread類,和wait()來自Object類。
(2)sleep是Thread的靜态類方法,誰調用的誰去睡覺,即使在a線程裡調用了b的sleep方法,實際上還是a去睡覺,
要讓b線程睡覺要在b的代碼中調用sleep。而wait()是Object類的非靜态方法
(3)sleep()釋放資源不釋放鎖,而wait()釋放資源釋放鎖;
(4)使用範圍:wait,notify和notifyAll隻能在同步控制方法或者同步控制塊裡面使用,而sleep可以在任何地方使用
10、多線程安全問題:
(1)原因:當程式的多條語句在操作線程共享資料時(如買票例子中的票就是共享資源),由于線程的随機性導緻
一個線程對多條語句,執行了一部分還沒執行完,另一個線程搶奪到cpu執行權參與進來執行,
此時就導緻共享資料發生錯誤。比如買票例子中列印重票和錯票的情況。
(2)解決方法:對多條操作共享資料的語句進行同步,一個線程在執行過程中其他線程不可以參與進來
11、Java中多線程同步是什麼?
同步是用來解決多線程的安全問題的,在多線程中,同步能控制對共享資料的通路。如果沒有同步,當一個線程在
修改一個共享資料時,而另外一個線程正在使用或者更新同一個共享資料,這樣容易導緻程式出現錯誤的結果。
12、什麼是鎖?鎖的作用是什麼?
鎖就是對象
鎖的作用是保證線程同步,解決線程安全問題。
持有鎖的線程可以在同步中執行,沒有鎖的線程即使獲得cpu執行權,也進不去。
13、同步的前提:
(1)必須保證有兩個以上線程
(2)必須是多個線程使用同一個鎖,即多條語句在操作線程共享資料
(3)必須保證同步中隻有一個線程在運作
14、同步的好處和弊端
好處:同步解決了多線程的安全問題
弊端:多線程都需要判斷鎖,比較消耗資源
15、同步的兩種表現形式:
(1)同步代碼塊:
可以指定需要擷取哪個對象的同步鎖,使用synchronized的代碼塊同樣需要鎖,但他的鎖可以是任意對象
考慮到安全問題,一般還是使用同一個對象,相對來說效率較高。
注意:
**雖然同步代碼快的鎖可以使任何對象,但是在進行多線程通信使用同步代碼快時,
必須保證同步代碼快的鎖的對象和,否則會報錯。
**同步函數的鎖是this,也要保證同步函數的鎖的對象和調用wait、notify和notifyAll的對象是
同一個對象,也就是都是this鎖代表的對象。
格式:
synchronized(對象)
{
需同步的代碼;
}
(2)同步函數
同步方法是指進入該方法時需要擷取this對象的同步鎖,在方法上使用synchronized關鍵字,
使用this對象作為鎖,也就是使用了目前對象,因為鎖住了方法,是以相對于代碼塊來說效率相對較低。
注:靜态同步函數的鎖是該方法所在的類的位元組碼檔案對象,即類名.class檔案
格式:
修飾詞 synchronized 傳回值類型 函數名(參數清單)
{
需同步的代碼;
}
在jdk1.5後,用lock鎖取代了synchronized,個人了解也就是對同步代碼塊做了修改,
并沒有提供對同步方法的修改,主要還是效率問題吧。
《JAVA大學簡明教程:執行個體程式設計》電子版免費下載下傳