如果一個類的對象在多線程程序中沒有導致競爭狀態,則稱這樣的類爲線程安全的( thread-safe) 。
多線程的同步問題指的是多個線程同時修改一個數據的時候,可能導致的問題多線程的問題,又叫 Concurrency
問題
1. 線程同步概念
當兩個任務以一種會引起衝突的方式訪問一個公共資源時,會引起競爭
。
package Thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AccountWithoutSync {
private static Account account = new Account();
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
for(int i = 0; i < 100; i++) //創建100 個在線程池executor 中執行的線程
executor.execute(new AddPennyTask());
executor.shutdown();
while(!executor.isTerminated()) { //測試線程是否終止
}
System.out.println("what is balance?" + account.getBalance());
}
private static class AddPennyTask implements Runnable{
public void run() {
account.deposit(1);
}
}
private static class Account{
private int balance = 0;
public int getBalance() {
return balance;
}
public void deposit(int amount) {
int newBalance = balance + amount;
try {
Thread.sleep(5);
}
catch(InterruptedException ex) {
}
balance = newBalance;
}
}
}
2. synchronized
爲避免競爭狀態,應該防止多個線程同時進入程序的某一特定部分,程序中的這部分稱爲臨界區( critical regioD) 。
可以使用關鍵字synchronized 來同步方法,以便一次只有一個線程可以訪問這個方法。
對於競爭衝突問題,有幾種辦法可以解決。
-
一種辦法是通過在deposit 方法中添加關鍵宇synchronized ,使Account 類成爲線程安全的.public synchronized void deposit(int amount)
-
一個同步方法在執行之前需要 加鎖( 15.3內容 )。
Object someObject =new Object();
synchronized (someObject){
//此處的代碼只有佔有了someObject後纔可以執行
}
synchronized 表示當前線程獨佔對象 someObject
當前線程獨佔了對象someObject,如果有其他線程試圖佔有對象someObject,就會等待,直到當前線程釋放對someObject的佔用。
someObject 又叫同步對象,所有的對象,都可以作爲同步對象。爲了達到同步的效果,必須使用同一個同步對象
釋放同步對象的方式: synchronized 塊自然結束,或者有異常拋出
3. 同步語句
當執行方法中某一個代碼塊時,同步語句不僅可用於對 this 對象加鎖,而且可用於對任何對象加鎖。這個代碼塊稱爲同步塊。
synchronized (expr) {
statements;
}
表達式expr 求值結果必須是一個對象的引用。如果對象已經被另一個線程鎖定,則在
解鎖之前,該線程將被阻塞。當獲准對一個對象加鎖時,該線程執行同步塊中的語句,然後解除給對象所加的鎖。
4. 線程安全類
如果一個類,其方法都是有synchronized修飾的,那麼該類就叫做線程安全的類
同一時間,只有一個線程能夠進入 這種類的一個實例 的去修改數據,進而保證了這個實例中的數據的安全(不會同時被多線程修改而變成髒數據)
比如StringBuffer和StringBuilder的區別
StringBuffer的方法都是有synchronized修飾的,StringBuffer就叫做線程安全的類
而StringBuilder就不是線程安全的類