當多個執行緒同時共用同一全域性變數或靜態變數時,在做讀操作時不會傳送資料衝突,而在做寫操作時可能會傳送資料衝突問題,就會出現執行緒安全問題。
背景: 某工廠需拖運100T貨物,找了3家託運公司,用多執行緒模擬託運情況。
程式碼如下:
/**
* @author MuXin
* @date 2020/10/21 16:11
*
* 某工廠需拖運100T貨物,找了3家託運公司,用多執行緒模擬託運情況。
*/
public class ThreadDemo {
public static void main(String[] args) {
ThreadGoods threadGoods = new ThreadGoods();
Thread threadA = new Thread(threadGoods, "A公司");
Thread threadB = new Thread(threadGoods, "B公司");
Thread threadC = new Thread(threadGoods, "C公司");
threadA.start();
threadB.start();
threadC.start();
}
static class ThreadGoods implements Runnable {
//貨物總量
private int goods = 100;
@Override
public void run() {
while (goods > 0) {
try {
//等待0.1s
Thread.sleep(100);
} catch (Exception e) {
}
//執行運貨操作
System.out.println(Thread.currentThread().getName()+",搬運第"+(101-goods)+"T貨物");
goods--;
}
}
}
}
輸出結果:
當多個執行緒共用同一個全域性成員變數時,做寫的操作可能會發生資料衝突問題。
將可能出現問題的程式碼塊包裹起來。
/**
* @author MuXin
* @date 2020/10/21 16:11
* <p>
* 某工廠需拖運100T貨物,找了3家託運公司,用多執行緒模擬託運情況。
*/
public class ThreadDemo {
public static void main(String[] args) {
ThreadGoods threadGoods = new ThreadGoods();
Thread threadA = new Thread(threadGoods, "A公司");
Thread threadB = new Thread(threadGoods, "B公司");
Thread threadC = new Thread(threadGoods, "C公司");
threadA.start();
threadB.start();
threadC.start();
}
static class ThreadGoods implements Runnable {
//自定義多執行緒同步鎖
private Object mutex = new Object();
//貨物總量
private int goods = 100;
@Override
public void run() {
while (goods > 0) {
try {
//等待0.1s
Thread.sleep(100);
} catch (Exception e) {
}
synchronized (mutex) {
if(goods > 0) {
try {
//等待0.1s
Thread.sleep(100);
} catch (Exception e) {
}
//執行運貨操作
System.out.println(Thread.currentThread().getName() + ",搬運第" + (101 - goods) + "T貨物");
goods--;
}
}
}
}
}
}
執行結果:
在方法上修飾synchronized稱為同步函數;
synchronized使用的鎖是this鎖。
/**
* @author MuXin
* @date 2020/10/21 16:11
* <p>
* 某工廠需拖運100T貨物,找了3家託運公司,用多執行緒模擬託運情況。
*/
public class ThreadGoods implements Runnable {
//貨物總量
private int goods = 100;
//自定義多執行緒同步鎖
private Object mutex = new Object();
@Override
public void run() {
while (goods > 0) {
try {
//等待0.1s
Thread.sleep(100);
} catch (Exception e) {
}
moveGoods();
}
}
public synchronized void moveGoods() {
if (goods > 0) {
try {
//等待0.1s
Thread.sleep(100);
} catch (Exception e) {
}
//執行運貨操作
System.out.println(Thread.currentThread().getName() + ",搬運第" + (101 - goods) + "T貨物");
goods--;
}
}
}
執行結果:
在方法上加static關鍵字,使用synchronized關鍵詞修飾,或者使用類.class檔案;
靜態同步函數使用的鎖是該函數所屬位元組碼檔案物件。
/**
* @author MuXin
* @date 2020/10/21 16:11
* <p>
* 某工廠需拖運100T貨物,找了3家託運公司,用多執行緒模擬託運情況。
*/
public class ThreadGoods implements Runnable {
//貨物總量
private int goods = 100;
//自定義多執行緒同步鎖
private Object mutex = new Object();
@Override
public void run() {
while (goods > 0) {
try {
//等待0.1s
Thread.sleep(100);
} catch (Exception e) {
}
moveGoods();
}
}
public void moveGoods() {
synchronized (ThreadGoods.class) {
if (goods > 0) {
try {
//等待0.1s
Thread.sleep(100);
} catch (Exception e) {
}
//執行運貨操作
System.out.println(Thread.currentThread().getName() + ",搬運第" + (101 - goods) + "T貨物");
goods--;
}
}
}
}
執行結果:
在同步中巢狀同步,俗稱套娃,導致鎖無法釋放。
1. 加鎖順序(執行緒按照一定的順序加鎖)
2. 加鎖時限(執行緒嘗試獲取鎖的時候加上一定的時限,超過時限則放棄對該鎖的請求,並釋放自己佔有的鎖)
3. 死鎖檢測
當多個執行緒同時共用,同一個全域性變數或靜態變數,做寫的操作時,可能會發生資料衝突問題,也就是執行緒安全問題。
1. 使用同步程式碼塊;
2. 使用 synchronized 鎖;
3. 使用靜態同步函數。
當會發生資料衝突問題時,使得同一時間只能執行一個執行緒,其他執行緒處於阻塞狀態,在執行完之後釋放鎖,交給下一個執行緒執行。
在多執行緒情況下,當多個執行緒共用統一資源時,不會受到其他執行緒的影響。
就是將可能出現執行緒安全的程式碼包裹起來,使得同一時間只能有一個執行緒執行被包裹的程式碼,其他執行緒處於阻塞狀態。
同步程式碼塊是自定義鎖(明鎖)
同步函數是使用的 this 鎖
同步函數使用的 this 鎖;
靜態同步函數使用的是位元組碼檔案,即類.class
同步中巢狀同步,俗稱套娃。