線程安全問題
關於線程安全問題,就是指在高併發的情況下多個線程操作同一個資源的時候,就會出現該資源被多次修改導致出現與預期不一致的結果。
爲了處理這種問題,就必須使用同步,所謂同步就是指多個操作在同一個時間段內只能又一個線程線程進行,其他線程要等待此線程完成之後纔可以繼續執行。需要使用到 synchronized 關鍵字。
synchronized關鍵字
關鍵字 synchronized 有兩種用法:
- 在方法中定義。
- 定義方法:
public synchronized void method()
- 定義方法:
- 使用 synchronize 塊(同步塊)。
- 同步塊:
public void method(){
synchronized(表達式){
}
}
代碼示例
同步代碼塊
使用 synchronized
關鍵字來鎖定類中所有的同步非靜態方法,需要使用 this
作爲 synchronized
塊的參數傳入 synchronized
中。
class MySyncBlockThread implements Runnable {
private int ticket = 10;
@Override
public void run() {
for (int i = 0; i < 11; i++) {
// 同步塊必須鎖一個對象,一般我們鎖當前對象
// 當前操作每次只允許一個對象進入
synchronized (this) {
if (this.ticket > 0) {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "->" + this.ticket--);
}
}
}
}
}
public class SyncBlock {
public static void main(String[] args) {
MySyncBlockThread mt = new MySyncBlockThread();
new Thread(mt, "票販子A").start();
new Thread(mt, "票販子B").start();
new Thread(mt, "票販子C").start();
}
}
控制檯輸出:
票販子A->10
票販子C->9
票販子C->8
票販子B->7
票販子C->6
票販子A->5
票販子C->4
票販子C->3
票販子C->2
票販子B->1
同步方法
class MySyncBlockThread implements Runnable {
private int ticket = 10;
@Override
public void run() {
for (int i = 0; i < 11; i++) {
sale();
}
}
// 同步方法
public synchronized void sale() {
if (this.ticket > 0) {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "->" + this.ticket--);
}
}
}
public class SyncBlock {
public static void main(String[] args) {
MySyncBlockThread mt = new MySyncBlockThread();
new Thread(mt, "票販子A").start();
new Thread(mt, "票販子B").start();
new Thread(mt, "票販子C").start();
}
}
控制檯輸出:
由於系統調度的不一樣,每次打印的結果也不一樣
票販子A->10
票販子A->9
票販子A->8
票販子A->7
票販子A->6
票販子C->5
票販子C->4
票販子C->3
票販子C->2
票販子C->1
同步代靜態方法
同步靜態方法,不管你有多少個類實例,同時只有一個線程能獲取鎖進入這個方法。
同步靜態方法是類級別的鎖,一旦任何一個線程進入這個方法,其他所有線程將無法訪問這個類的任何同步類鎖的方法。
// 用在靜態方法
private synchronized static void staticMethod() {
System.out.println("staticMethod");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}