03. 傳統線程互斥技術
線程安全問題例子:銀行轉賬
同一個賬戶一邊進行出賬操作(自己交學費),另一邊進行入賬操作(別人給自己付款),線程不同步帶來的安全問題
示例:逐個字符的方式打印字符串
class Outputer
{
public void output(String name)
{
int len =name.length();
for (int i=0; i<len; i++)
SOP(name.charAt(i));逐個字符打印
SOP();換行
}
}
public void test()
{
Outputeroutputer = new Outputer();
newThread(
new Runnable()
{
public void run()
{
Thread.sleep(100);
outputer.output(“zhangxiaoxiang”);
}
}).start();
newThread(
new Runnable()
{
public void run()
{
Thread.sleep(100);
outputer.output(“lihuoming”);
}
}).start();
}
注意:
內部類不能訪問局部變量,要訪問需加final
靜態方法中不能創建內部類的實例對象
打印結果發現的問題:線程不同步所致,兩個線程都在使用同一個對象
要避免下邊產生的問題,左邊方法體中的代碼要實現原子性
有一個線程正在使用這個方法的代碼,別的線程就不能再使用。
就和廁所裏的坑一樣,已經有人在用了,別人就不能再去用了。
Java中某段代碼要實現排他性,就將這段代碼用synchronized關鍵字保護起來。
同步鎖可以用任意對象,相當於門鎖
synchronized(name)
{
for (int i=0; i<len; i++)
SOP(name.charAt(i));逐個字符打印
SOP();換行
}
這樣的話,有一個線程進入保護區域後,沒出來的話,別的線程就不能進入保護區域。
互斥方法:
a、同步代碼塊
synchronized(lock){}
b、同步方法
方法返回值前加synchronized
同步方法上邊用的鎖就是this對象
靜態同步方法使用的鎖是該方法所在的class文件對象
使用synchronized關鍵字實現互斥,要保證同步的地方使用的是同一個鎖對象
public synchronized void output(String name)
{
int len =name.length();
這裏就不要再加同步了,加上極易出現死鎖
for (int i=0; i<len; i++)
SOP(name.charAt(i));逐個字符打印
SOP();換行
}
04. 傳統線程同步通信技術
面試題,子線程10次與主線程100次來回循環執行50次
下面是我剛看完面試題就暫停視頻自己試着寫的代碼,還可以,結果完成要求了
在單次循環結束後讓這個剛結束循環的線程休眠,保證另一個線程可以搶到執行權。
public class ThreadInterViewTest
{
/**
* 剛看到面試題沒看答案之前試寫
* 子線程循環10次,回主線程循環100次,
* 再到子線程循環10次,再回主線程循環100次
* 如此循環50次
*/
publicstatic void main(String[] args)
{
intnum = 0;
while(num++<50)
{
newThread(new Runnable()
{
@Override
public void run()
{
circle("子線程運行", 10);
}
}).start();
try
{
//加這句是保證上邊的子線程先運行,剛開始沒加,主線程就先開了
Thread.sleep(2000);
}catch (InterruptedException e)
{
e.printStackTrace();
}
circle("主線程", 100);
}
}
publicstatic synchronized void circle(String name, int count)
{
for(int i=1; i<=count; i++)
{
System.out.println(name+"::"+i);
}
try
{
Thread.sleep(5000);
}catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
張老師講的方法:
1、將子線程和主線程中要同步的方法進行封裝,加上同步關鍵字實現同步
2、兩個線程間隔運行,添加一個標記變量進行比較以實現相互通信,加色的部分
wait notify notifyAll wait會拋出異常
class Business
{
private boolean bShouleSub = true;
publicsynchronized void sub()
{
if (bShouleSub)
{
for (int i=1; i<11; i++)
SOP(sub+i);
bShouldSub= false;
this.notify();
}
else
this.wait();
}
publicsynchronized void main()
{
if (!bShouldSub)
{
for (int i=1; i<101; i++)
SOP(main+i);
bShouldSub= true;
this.notify();
}
else
this.wait();
}
}
經驗:要用到共同數據(包括同步鎖)或相同算法的多個方法要封裝在一個類中
鎖是上在代表要操作的資源類的內部方法中的,而不是上在線程代碼中的。這樣寫出來的類就是天然同步的,只要使用的是同一個new出來的對象,那麼這個對象就具有同步互斥特性
判斷喚醒等待標記時使用while增加程序健壯性,防止僞喚醒
此處使用while以增加程序健壯性,因爲存在虛假喚醒,有時候並沒有被notify就醒了。如果該方法沒有同步的話,此處就更要使用while進行判斷了,避免進程不同步問題