synchronized用於多線程訪問,並且被修飾的部分不能同時被執行,是代碼同步的一種方式。
1 使用synchronized修飾方法
1.1 synchronized修飾方法原理
- 過程:當多個線程同時訪問被synchronized修飾的方法是,有且僅有一個線程可以被訪問,當一個線程在訪問時,其它線程只能等待。當一個線程訪問完畢後,下一個線程纔可以訪問。
- 原理:當方法被synchronized修飾後,如果想要執行該方法就必須獲得相應的鎖。每個類有且僅有一個鎖(針對靜態方法),每個類的實例也是有且僅有一個鎖。當多個線程在同時訪問同一個方法時,執行該方法就必須獲得相應的鎖,同時鎖只有一個,所以只能有一個線程可以獲得鎖,其它的線程必須等待該線程釋放鎖後才能獲取到該鎖。
- 進階說明:由於每個類只有一個鎖,所以當一個類中有多個方法被synchronized修飾時,在同一時間內只能有一個方法可以獲得鎖,所以只有一個被synchronized修飾的方法可以執行。
1.2 synchronized修飾方法示例
public void showDo(String msg){
for(int i=0;i<1000000;i++){
if (i%100000==0){
System.out.println("打印結果"+msg+i/100000);
}
}
}
//使用
new Thread(){
@Override
public void run() {
super.run();
showDo("線程一");
}
}.start();
new Thread(){
@Override
public void run() {
super.run();
showDo("線程二");
}
}.start();
結果:
未加synchronized修飾方法,可以看到執行順序是打亂的,無序的。加了synchronized後:
public synchronized void showDo(String msg){
for(int i=0;i<1000000;i++){
if (i%100000==0){
System.out.println("打印結果"+msg+i/100000);
}
}
}
結果
2 使用synchronized修飾代碼塊
2.1使用synchronized修飾代碼塊說明
當使用synchronized在修飾代碼塊的時候需要一個自定義鎖,當在多線程訪問代碼塊的時候,只要獲得自定義鎖就可以執行。自定義鎖可以是一個類,也可以是一個實例(可以是Object的子類,也可以是當前類自己),當具有相同自定義鎖時代碼塊會順序執行,當鎖不同的時候互不影響。
2.2 使用synchronized修飾代碼塊示例
private static String s1 = "";
private static String s2 = "aa";
public void showDo(String msg) {
synchronized (s1){
for (int i = 0; i < 1000000; i++) {
if (i % 100000 == 0) {
System.out.println("打印結果" + msg + i / 100000);
}
}
}
}
public void showDo1(String msg) {
synchronized (s2){
for (int i = 0; i < 1000000; i++) {
if (i % 100000 == 0) {
System.out.println("打印" + msg + i / 100000);
}
}
}
}
//調用
new Thread() {
@Override
public void run() {
super.run();
showDo1("線程一");
}
}.start();
new Thread() {
@Override
public void run() {
super.run();
showDo("線程二");
showDo1("線程二");
}
}.start();
結果:
由上可得,多個同步鎖,只有競爭同一個同步鎖纔會需要等待,不是競爭同一個鎖的代碼塊互不影響。
- synchronized不能修飾構造函數
- 定義接口方法時不能使用synchronized
- synchronized(this)鎖的是當前對象,當前有幾個對象,this就有多少份
- synchronized(XX.class)這個與當前對象無關,只要鎖是XX.class的都會被同步
- 如果同一個類中有多個方法使用了同步鎖synchronized(this)或者多個方法被synchronized修飾,則多個線程訪問該類中同步方法時,每次只能訪問一個,其它的被阻塞。如:
public synchronized void A(){
......
}
public synchronized void B(){
......
}
有兩個線程分別訪問同一個對象T的A方法和B方法,則同時只能有一個方法被其中一個線程訪問,另一個線程處於阻塞狀態,因爲方法A和B持有同一個對象鎖,synchronized(this)也是類似的情況。
- 同一個類中有多個方法使用了同步鎖synchronized修飾,且這些類是靜態的,因爲靜態方法是屬於類的,而不是屬於某個對象的,所以它與synchronized(XX.class)類似。
public synchronized static void method() {
// todo
}
- 每個對象只有一個鎖(lock)與之相關聯,誰拿到這個鎖誰就可以運行它所控制的那段代碼。
- 實現同步是要很大的系統開銷作爲代價的,甚至可能造成死鎖,所以儘量避免無謂的同步控制。