在多線程的情況下,由于同一進程的多個線程共享同一片存儲空間,在帶來方便的同時,也帶來了訪問沖突這個嚴重的問題。Java語言提供了專門機制以解決這種沖突,有效避免了同一個數據對象被多個線程同時訪問。
由于我們可以通過 private 關鍵字來保證數據對象只能被方法訪問,所以我們只需針對方法提出一套機制,這套機制就是 synchronized 關鍵字,它包括兩種用法:synchronized 方法和 synchronized 塊。
1. synchronized 方法:通過在方法聲明中加入 synchronized關鍵字來聲明 synchronized 方法。如:
public synchronized void accessVal(int newVal);
synchronized 方法控制對類成員變量的訪問:每個類實例對應一把鎖,每個 synchronized 方法都必須獲得調用該方法的類實例的鎖方能執行,否則所屬線程阻塞,方法一旦執行,就獨占該鎖,直到從該方法返回時才將鎖釋放,此后被阻塞的線程方能獲得該鎖,重新進入可執行狀態。這種機制確保了同一時刻對于每一個類實例,其所有聲明為 synchronized 的成員函數中至多只有一個處于可執行狀態(因為至多只有一個能夠獲得該類實例對應的鎖),從而有效避免了類成員變量的訪問沖突(只要所有可能訪問類成員變量的方法均被聲明為 synchronized)。
在 Java 中,不光是類實例,每一個類也對應一把鎖,這樣我們也可將類的靜態成員函數聲明為 synchronized ,以控制其對類的靜態成員變量的訪問。
synchronized 方法的缺陷:若將一個大的方法聲明為synchronized 將會大大影響效率,典型地,若將線程類的方法 run() 聲明為 synchronized ,由于在線程的整個生命期內它一直在運行,因此將導致它對本類任何 synchronized 方法的調用都永遠不會成功。當然我們可以通過將訪問類成員變量的代碼放到專門的方法中,將其聲明為 synchronized ,并在主方法中調用來解決這一問題,但是 Java 為我們提供了更好的解決辦法,那就是 synchronized 塊。
2. synchronized 塊:通過 synchronized關鍵字來聲明synchronized 塊。語法如下:
synchronized(syncObject) {
//允許訪問控制的代碼
}
synchronized 塊是這樣一個代碼塊,其中的代碼必須獲得對象 syncObject (如前所述,可以是類實例或類)的鎖方能執行,具體機制同前所述。由于可以針對任意代碼塊,且可任意指定上鎖的對象,故靈活性較高。
public
?class
?TraditionalThreadSynchronized? {
????
?
????
public
?static
?void
?main(String[] args) {
????????
new
?TraditionalThreadSynchronized().init();
????
}
????
void
?init(){
????????
final
?Outputer out? =
new
?Outputer();
????????
????????
new
?Thread(
new
?Runnable(){
????????????
@Override
????????????
public
?void
?run() {
????????????????
while
(
true
){
????????????????????
try
?{
????????????????????????
Thread.sleep(
200
);
????????????????????
}
catch
?(InterruptedException e) {
????????????????????????
e.printStackTrace();
????????????????????
}
????????????????????
out.output1(
"Tmethod--->AAAAAA"
);
????????????????
}
????????????
}
?????????????
?
????????
}).start();
????????
????????
new
?Thread(
new
?Runnable(){
????????????
@Override
????????????
public
?void
?run() {
????????????????
while
(
true
){
????????????????????
try
?{
????????????????????????
Thread.sleep(
200
);
????????????????????
}
catch
?(InterruptedException e) {
????????????????????????
e.printStackTrace();
????????????????????
}
????????????????????
out.output3(
"Tmethod--->BBBBBB"
);
????????????????
}
????????????
}
????????????
?
????????
}).start();
????
}
????
?
????
static
?class
?Outputer{?
????????
/**
?????????
* 方法內部加上線程鎖,線程的鎖只能鎖同一個對象,如果有static方法那么線程鎖必須為Class,不能為this
?????????
*/
????????
public
?void
?output1(String name){
????????????
int
?len =name.length();
????????????
synchronized
?(Outputer.
class
){
????????????????
for
(
int
?i=
0
;i<len;i++){
????????????????????
System.out.print(name.charAt(i));
????????????????
}
????????????????
System.out.println();
????????????
}
????????
}
????????
/**
?????????
* output2 和 output3不能實現同步,output2用的是對象鎖的是this而output3是static方法要鎖的話只能用class,如:output1
?????????
* @param name
?????????
*/
????????
public
?synchronized
?void
?output2(String name){
????????????
int
?len =name.length();
????????????????
for
(
int
?i=
0
;i<len;i++){
????????????????????
System.out.print(name.charAt(i));
????????????????
}
????????????????
System.out.println();
????????
}
????????
????????
public
?static
?synchronized
?void
?output3(String name){
????????????
int
?len =name.length();
????????????
for
(
int
?i=
0
;i<len;i++){
????????????????
System.out.print(name.charAt(i));
????????????
}
????????????
System.out.println();
????????
}
????
}
}