多線程技術
1.多線程概述
(1)進程:正在執行中的程序,比如QQ,javac等
(2)線程:進程中的一個獨立控制單元,線程控制着進程的執行。一個進程至少有一個線
(3)多線程的好處:可使程序產生同時運行的效果,多線程下載可提高效率。
2.多線程的兩種創建方式:
第一種方式:繼承Thread類
創建步驟:
(1)定義一個類繼承Thread(那麼此類也就變成了Thread類,擁有Start方法。
(2)複寫Thread類中的run方法(將需要使用多線程運行的代碼存在run方法,讓線程運行)
(3)調用線程的start方法.
注意:start():開啓一個新線程並運行run方法中的代碼,
而調用run方法只是對象調用方法,而不會創建新的線程。
第二種方式:實現Runnable接口。
創建步驟:
(1) 定義類實現Runnable接口
(2)覆蓋Runnable接口中的run方法。(還沒有擁有線程)
(3)通過Thread創建線程對象。
(4)將Runnable接口的子類對象作爲實際參數傳遞給Thread類的構造函數。
(5)調用Thread類的Start方法開啓線程,Runnable接口的run方法即可運行。
3.實現方式和繼承方式的區別
(1)繼承了一個類不能再繼承其他類,java只支持單繼承,這樣就會出現類繼承的侷限性。
(2)繼承Thread:線程代碼存放在Thread類的run方法中,直接就可以調用子類的Start方法即可開啓線程。
(3)實現Runnable:線程代碼存放在Runnable接口的子類的run方法中,開啓線程還需要new Thread();
(4)在定義線程時,建議使用實現方式。
4.線程的狀態和獲取線程對象以及名稱
線程的狀態: 1被創建——2運行——3阻塞(臨時狀態,具備執行資格但沒執行)——4凍結———5消亡。
獲取都有自己的名稱:Thread-編號從0開始
獲取對象方法:
static Thread currentThread()獲取當前線程對象。getName()獲取線程名稱。setName()設置線程名稱。
5.線程的安全解決
(1)產生安全問題的原因:當多個線程在操作同一個共享數據時,
一個線程還沒執行完,另一個線程參與進來,導致共享數據產生了錯誤。
(2)解決辦法:
同步代碼塊: synchronized(鎖對象){需要被同步的代碼數據;}
<1>對象如同鎖,持有鎖的線程纔可以在同步中執行。
<2>同步的前提:必須要有兩個或兩個以上的線程,必須是多個線程使用的同一個鎖對象。
同步函數:
非靜態同步函數的鎖:this
函數需要被對象調用,那麼函數都有一個所屬對象引用就是this,所以同步函數使用的鎖是this.
靜態同步函數的鎖:class對象
因爲靜態方法中不可以定義this,所以鎖不可能是this。靜態進內存時,內存中沒有本類對象,
但是一定有該類對應的字節碼文件對象,類名.class,該對象的類型是class.
6死鎖:同步中鉗套同步容易出現死鎖現象,應該避免這樣現象發生。
class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag = flag;
}
public void run()
{
if(flag)
{
while(true)
{
synchronized(MyLock.locka)
{
System.out.println(Thread.currentThread().getName()+"...if locka ");
synchronized(MyLock.lockb)
{
System.out.println(Thread.currentThread().getName()+"..if lockb");
}
}
}
}
else
{
while(true)
{
synchronized(MyLock.lockb)
{
System.out.println(Thread.currentThread().getName()+"..else lockb");
synchronized(MyLock.locka)
{
System.out.println(Thread.currentThread().getName()+".....else locka");
}
}
}
}
}
}
class MyLock
{
static Object locka = new Object();
static Object lockb = new Object();
}
class DeadLockTest
{
public static void main(String[] args)
{
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
t2.start();
}
}
7.線程間通信
(1)線程間通信:
其實就是多個線程在操作同一個資源,但操作的動作不同。
(2)等待喚醒機制:
<1>wait(),notify(),notifyAll()都使用在同步中,因爲只有同步才具有鎖,所以要對持有鎖的線程操作。
<2>這些用來操作線程的方法爲什麼定義在Object類中?
a.這些方法存在於同步中。
b.使用這些方法時必須要標識所屬的同步的鎖。
c.等待和喚醒必須是同一個鎖。
d.鎖可以是任意對象,所以任意對象調用的方法一定定義在Object中。
(3)wait(),sleep()的區別:
wait():釋放資源,釋放鎖。
sleep():釋放資源,不釋放鎖。
(3)舉例:生產者與消費者
<1>對於多個生產者和消費者,爲讓被喚醒的線程再一次判斷標記,故要定義while判斷標記。
<2>爲什麼定義notifyAll?
因爲需要喚醒對方線程,只用notify容易出現只喚醒本方線程的情況,導致程序中的所有
線程都等待。
<3>JDK1.5提供的多線程升級解決方案。
將同步synchronized替換成現實Lock操作。將Object中的wait,notify,notifyAll替換成了
condition對象。該對象可以用Lock鎖進行獲取。
8.停止線程:
(1)stop方法已經過時。
(2)如何停止線程?
只有一種,就是讓run方法結束,開啓多線程運行,運行代碼通常是循環結構。只要控制住循環,
就可以讓run方法結束,也就是線程結束。
(3)特殊情況:
<1>當線程處於了凍結狀態,就不會讀到標記,那麼線程就不會結束。
<2>當沒有指定的方式讓凍結的線程恢復到運行狀態時,這時需要對凍結進行清除,強制讓線程恢復
到運行狀態中來,這樣就可以操作標記讓線程結束。
(4)Thread類提供該方法:interrupt()
9.守護線程:
setDaemon:
public final void setDaemon(boolean on)
(1)當正在運行的線程都是守護線程時,JVM退出。
(2)該方法必須在啓動線程前調用。
(3)主線程是前臺線程,前臺線程一結束,守護線程自動結束。
10.join方法:
join://搶奪CPU執行權
public final void join()
throws InterruptedException
等待該線程終止
(1)當A線程執行到了B線程的join()方法時,A就會等待,等B線程都執行完,A纔會執行。
(2)join可以用來臨時加入線程執行。
11.優先級&yield方法
setPriority(int newPriority)
更改線程的優先級(默認是5)
(1) MAX_PRIORITY-->10
NORM_PRIORITY--->5
MIN_PRIORITY--->1
(2)注:在任務管理器的進程中,也可進行優先級設置。
yield:臨時停止
public static void yield()
暫停當前正在執行的線程對象,並執行其他線程。yield可稍微減緩線程的運行,
使線程達到接近平均運行的效果。