1.創建線程的第二種方式:實現Runable接口
步驟:
- 定義類實現Runnable接口
- 覆蓋Runnable接口中的run方法。 將線程要運行的代碼存放在該run方法中。
- 通過Thread類建立線程對象。
- 將Runnable接口的子類對象作爲實際參數傳遞給Thread類的構造函數。 爲什麼要將Runnable接口的子類對象傳遞給Thread的構造函數。 因爲,自定義的run方法所屬的對象是Runnable接口的子類對象。 所以要讓線程去指定指定對象的run方法。就必須明確該run方法所屬對象。
- 調用Thread類的start方法開啓線程並調用Runnable接口子類的run方法
實現方式和繼承方式有什麼區別呢?
實現方式好處:避免了單繼承的侷限性。實現了Runable後還可以extends 其他類
在定義線程時,建立使用實現方式。
兩種方式區別:
繼承Thread:線程代碼存放Thread子類run方法中。
實現Runnable,線程代碼存在接口的子類的run方法。
2.同步函數都要被對象調用,因此同步函數使用的鎖是this; 靜態同步函數所用的鎖是對象所屬的類的字節碼文件對象(類名.class)。
3.死鎖:a對象拿着A鎖,b對象拿着B鎖,a對象要進要進B鎖對應的代碼中去,b對象要進A鎖對應的代碼中來,兩者相持不下
死鎖一般發生情況:同步中嵌套同步。死鎖
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();
}
}
5.
wait(); notify(); notifyAll();都使用在同步中,因爲要對持有監視器(鎖)的線程操作。所以要使用在同步中,因爲只有同步才具有鎖。
爲什麼這些操作線程的方法要定義Object類中呢?
因爲這些方法在操作同步中線程時,都必須要標識它們所操作線程只有的鎖,只有同一個鎖上的被等待線程,可以被同一個鎖上notify喚醒。不可以對不同鎖中的線程進行喚醒。也就是說,等待和喚醒必須是同一個鎖。而鎖可以是任意對象,所以可以被任意對象調用的方法定義Object類中。
6.(P146)多個消費者和生產者在操作同一資源的時候出現的情況:後面生產的將前面覆蓋。
產生原因:未判斷標記便進行打印 解決辦法:循環判斷標記,然後在喚醒的時候喚醒所有線程(notifyAll),因爲只用notify,容易出現只喚醒本方線程的情況,導致程序中的所有線程都等待。
7.jdk1.5中提供了多線程升級解決方案,將同步synchronized替換成了Lock操作。將Object中的wait,notify,notifyAll,替換成了Condition對象,該對象可以通過Lock鎖進行獲取。