多線程操作共享的數(shù)據(jù),例如買票問題巷帝,可能會出現(xiàn)相同的票,或者負(fù)數(shù)的票。出現(xiàn)這種問題的條件
1. 多線程環(huán)境
2. 多線程之間共享數(shù)據(jù)
3. 對共享數(shù)據(jù)的操作不是原子性的(原子性:不可分割)
多線程操作同一數(shù)據(jù)源汪榔,對數(shù)據(jù)的操作中斷了(線程的時間片結(jié)束),另一個線程進(jìn)來了
public void run() {
// 賣同一張票的情況肃拜?
// 賣第 0 張 第 -1 張票的情況痴腌?
while (true) {
if (count >= 1) {
// t3 在這里時間片到了,返回就緒狀態(tài)
System.out.println(Thread.currentThread().getName() + "售出第 " + count + " 張票");
try {
Thread.sleep(100); // t1 t2 走到這里燃领,
} catch (InterruptedException e) {
e.printStackTrace();
}
count--; // coumt-- count++ 它不是原子性操作 count = count - 1
}
}
}
解決辦法-將對共享數(shù)據(jù)的非原子性操作“變成”原子性操作
05-2-1線程同步方式
- 同步代碼塊
- 同步方法
- Lock鎖機(jī)制
05-2.1線程同步問題操作
-
同步代碼塊
格式
synchronized(鎖對象){
需要同步的代碼
}
同步鎖:
1. 代碼塊中的鎖對象可以是任意對象
2. 但是必須保證多個線程使用的鎖對象是同一個
* 鎖對象作用:只讓一個線程在同步代碼塊中執(zhí)行
創(chuàng)建的鎖對象士聪,必須在run方法的外部,使得唯一猛蔽,如果在run方法內(nèi)部剥悟,那么各個線程執(zhí)行run方法的時候,都會創(chuàng)建將要使用的鎖對象曼库,鎖都一不一樣区岗,沒用。原理毁枯,是到了同步方法慈缔,會檢查是否有鎖,有的話种玛,就拿鎖進(jìn)去胀糜,直到執(zhí)行完才歸還。當(dāng)另外線程執(zhí)行到同步方法蒂誉,檢查教藻,沒有鎖,阻塞右锨。
-
同步方法
格式
public synchroniczed void method{
可能會產(chǎn)生線程安全問題的代碼
}
同步方法會把方法內(nèi)部的代碼鎖住括堤,只讓一個線程執(zhí)行
同步方法的鎖對象:
也就是實(shí)現(xiàn)類對象(new RunnableImpl()
),也就是this
(代表當(dāng)前對象)
靜態(tài)代碼塊對象:本類的class對象-->class文件對象(類名.class)
-
Lock鎖
Lock
接口中方法,實(shí)現(xiàn)比Synchronized
方法和語句可獲得更廣泛的鎖的操作
Lock
接口的方法;
void lock
獲取鎖
void unLock
釋放鎖
ReentrantLock implements Lock
接口
使用步驟:- 在成員位置創(chuàng)建一個
ReentrantLock
對象 - 在可能出現(xiàn)安全問題的代碼前調(diào)用Lock接口中的方法lock獲取鎖
- 在可能出現(xiàn)安全問題的代碼后調(diào)用Lock接口中的方法unlock釋放鎖
- 在成員位置創(chuàng)建一個
l.lock();
線程安全的代碼
finally{
//無論程序是夠異常悄窃,都把鎖釋放
l.unLock();
}