Java小白入門 —— 多執行緒之間實作同步(二)
一. 什么是執行緒安全:
為什么會存在執行緒安全問題:
當多個執行緒同時共享同一全域變數或靜態變數時,在做讀操作時不會發送資料沖突,而在做寫操作時可能會發送資料沖突問題,就會出現執行緒安全問題,
二. 案例:
背景: 某工廠需拖運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--;
}
}
}
}
輸出結果:

當多個執行緒共享同一個全域成員變數時,做寫的操作可能會發生資料沖突問題,
三. 執行緒安全解決方案:
1. 使用同步代碼塊
將可能出現問題的代碼塊包裹起來,
/**
* @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--;
}
}
}
}
}
}
運行結果:

2. 使用同步函式(synchronized鎖)
在方法上修飾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--;
}
}
}
執行結果:

3. 靜態同步函式
在方法上加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. 如何避免死鎖:
1. 加鎖順序(執行緒按照一定的順序加鎖)
2. 加鎖時限(執行緒嘗試獲取鎖的時候加上一定的時限,超過時限則放棄對該鎖的請求,并釋放自己占有的鎖)
3. 死鎖檢測
五. 常見面試題:
1. 什么是執行緒安全
當多個執行緒同時共享,同一個全域變數或靜態變數,做寫的操作時,可能會發生資料沖突問題,也就是執行緒安全問題,
2. 如何解決多執行緒之間的執行緒安全問題?
1. 使用同步代碼塊;
2. 使用 synchronized 鎖;
3. 使用靜態同步函式,
3. 為什么使用執行緒同步或使用鎖之后能解決執行緒安全問題?
當會發生資料沖突問題時,使得同一時間只能執行一個執行緒,其他執行緒處于阻塞狀態,在執行完之后釋放鎖,交給下一個執行緒執行,
4. 什么是多執行緒之間的同步?
在多執行緒情況下,當多個執行緒共享統一資源時,不會受到其他執行緒的影響,
5. 什么是同步代碼塊?
就是將可能出現執行緒安全的代碼包裹起來,使得同一時間只能有一個執行緒執行被包裹的代碼,其他執行緒處于阻塞狀態,
6. 同步代碼塊和同步函式的區別?
同步代碼塊是自定義鎖(明鎖)
同步函式是使用的 this 鎖
7. 同步函式和靜態同步函式的區別?/ 例如現在一個靜態方法和一個非靜態靜態怎么實作同步?
同步函式使用的 this 鎖;
靜態同步函式使用的是位元組碼檔案,即類.class
8. 什么是死鎖?
同步中嵌套同步,俗稱套娃,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/186685.html
標籤:其他
上一篇:軟體測驗基礎理論知識
