synchronized的作用 
一、同步方法
public synchronized void methodAAA(){

//….

}
鎖定的是調(diào)用這個同步方法的對象

測試:
a、不使用這個關(guān)鍵字修飾方法,兩個線程調(diào)用同一個對象的這個方法。
目標(biāo)類:

1
public class TestThread {
2
    
public  void execute(){  //synchronized,未修飾
3
        for(int i=0;i<100;i++){
4
            System.out.println(i);
5
        }
    
6
    }

7
}


線程類:

1
public class ThreadA implements Runnable{
2
    TestThread test
=null;
3
    
public ThreadA(TestThread pTest){  //對象有外部引入,這樣保證是同一個對象
4
        test=pTest;
5
    }

6
    
public void run() {
7
        test.execute();
8
    }

9
}

調(diào)用:
1
TestThread test=new TestThread();
2
Runnable runabble
=new ThreadA(test);
3
Thread a
=new Thread(runabble,"A");                
4
a.start();
5
Thread b
=new Thread(runabble,"B");
6
b.start();


結(jié)果:
輸出的數(shù)字交錯在一起。說明不是同步的,兩個方法在不同的線程中是異步調(diào)用的。

b、修改目標(biāo)類,增加synchronized修飾

1
public class TestThread {
2
    
public synchronized  void execute(){  //synchronized修飾
3
        for(int i=0;i<100;i++){
4
            System.out.println(i);
5
        }
    
6
    }

7
}


結(jié)果:
輸出的數(shù)字是有序的,首先輸出A的數(shù)字,然后是B,說明是同步的,雖然是不同的線程,但兩個方法是同步調(diào)用的。
注意:上面雖然是兩個不同的線程,但是是同一個實例對象。下面使用不同的實例對象進(jìn)行測試。

c、每個線程都有獨立的TestThread對象。
目標(biāo)類:

1
public class TestThread {
2
    
public synchronized void execute(){  //synchronized修飾
3
        for(int i=0;i<100;i++){
4
            System.out.println(i);
5
        }
    
6
    }

7
}

線程類:
1
public class ThreadA implements Runnable{
2
    
public void run() {
3
        TestThread test
=new TestThread();
4
        test.execute();
5
    }

6
}

7

調(diào)用:
1
Runnable runabble=new ThreadA();
2
Thread a
=new Thread(runabble,"A");                
3
a.start();
4
Thread b
=new Thread(runabble,"B");
5
b.start();


結(jié)果:
輸出的數(shù)字交錯在一起。說明雖然增加了synchronized 關(guān)鍵字來修飾方法,但是不同的線程調(diào)用各自的對象實例,兩個方法仍然是異步的。

引申:
對于這種多個實例,要想實現(xiàn)同步即輸出的數(shù)字是有序并且按線程先后順序輸出,我們可以增加一個靜態(tài)變量,對它進(jìn)行加鎖(后面將說明鎖定的對象)。

修改目標(biāo)類:

 1
public class TestThread {
 2
    
private static Object lock=new Object(); //必須是靜態(tài)的。
 3
    public  void execute(){
 4
        
synchronized(lock){
 5
            
for(int i=0;i<100;i++){
 6
                System.out.println(i);
 7
            }
    
 8
        }

 9
    }

10
}

二、同步代碼塊

1
public void method(SomeObject so){
2
    
synchronized(so)
3
       
//…..
4
    }

5
}

鎖定一個對象,其實鎖定的是該對象的引用(object reference)
誰拿到這個鎖誰就可以運行它所控制的那段代碼。當(dāng)有一個明確的對象作為鎖時,就可以按上面的代碼寫程序,但當(dāng)沒有明確的對象作為鎖,只是想讓一段代碼同步時,可以創(chuàng)建一個特殊的instance變量(它必須是一個對象)來充當(dāng)鎖(上面的解決方法就是增加了一個狀態(tài)鎖)。

a、鎖定一個對象,它不是靜態(tài)的
private byte[] lock = new byte[0]; // 特殊的instance變量
目標(biāo)類:

 1
public class TestThread {
 2
    
private Object lock=new Object(); 
 3
    
public  void execute(){
 4
        
synchronized(lock){  //增加了個鎖,鎖定了對象lock,在同一個類實例中,是線程安全的,但不同的實例還是不安全的。
 5

 6
因為不同的實例有不同對象鎖lock
 7
            
for(int i=0;i<100;i++){
 8
                System.out.println(i);
 9
            }
    
10
        }

11
    }

12
}
  


其實上面鎖定一個方法,等同于下面的:

1
public void execute(){  
2
    
synchronized(this){   //同步的是當(dāng)然對象
3
        for(int i=0;i<100;i++){
4
            System.out.println(i);
5
        }
    
6
    }

7
}

b、鎖定一個對象或方法,它是靜態(tài)的
這樣鎖定,它鎖定的是對象所屬的類
public synchronized  static void execute(){
    //...
}
等同于

1
public class TestThread {
2
    
public static void execute(){
3
        
synchronized(TestThread.class){
4
            
//

5
        }

6
    }

7
}

測試:

目標(biāo)類:

 1
public class TestThread {
 2
    
private static Object lock=new Object();
 3
    
public synchronized static void execute(){  //同步靜態(tài)方法
 4
        for(int i=0;i<100;i++){
 5
            System.out.println(i);
 6
        }
    
 7
    }

 8
    
public static void execute1(){
 9
        
for(int i=0;i<100;i++){
10
            System.out.println(i);
11
        }
    
12
    }

13
    
public void test(){
14
        execute();     
//輸出是有序的,說明是同步的
15
        
//execute1();  //輸出是無須的,說明是異步的
16
    }

17
}

線程類:調(diào)用不同的方法,于是建立了兩個線程類

 1
public class ThreadA implements Runnable{
 2
    
public void run() {
 3
        TestThread.execute();
//調(diào)用同步靜態(tài)方法
 4
    }

 5
}

 6
public class ThreadB implements Runnable{
 7
    
public void run() {
 8
        TestThread test
=new TestThread();
 9
        test.test();
//調(diào)用非同步非靜態(tài)方法
10
    }

11
}

調(diào)用:

1
Runnable runabbleA=new ThreadA();
2
Thread a
=new Thread(runabbleA,"A");                
3
a.start();
4
Runnable runabbleB
=new ThreadB();
5
Thread b
=new Thread(runabbleB,"B");                
6
b.start();

注意:
1、用synchronized 來鎖定一個對象的時候,如果這個對象在鎖定代碼段中被修改了,則這個鎖也就消失了??聪旅娴膶嵗?/p>

目標(biāo)類:

 1
public class TestThread {
 2
     
private static final class TestThreadHolder {
 3
            
private static TestThread theSingleton = new TestThread();
 4
            
public static TestThread getSingleton() {
 5
                
return theSingleton;
 6
            }

 7
            
private TestThreadHolder() {
 8
            }

 9
        }

10
     
11
    
private Vector ve =null;
12
    
private Object lock=new Object();
13
    
private TestThread(){
14
        ve
=new Vector();
15
        initialize();
16
    }

17
    
public static TestThread getInstance(){
18
        
return TestThreadHolder.getSingleton();
19
    }

20
    
private void initialize(){
21
        
for(int i=0;i<100;i++){
22
            ve.add(String.valueOf(i));
23
        }

24
    }

25
    
public void reload(){
26
        
synchronized(lock){
27
            ve
=null;            
28
            ve
=new Vector();
29
                        
//lock="abc"; 
30
            for(int i=0;i<100;i++){
31
                ve.add(String.valueOf(i));
32
            }

33
        }

34
        System.out.println(
"reload end");
35
    }

36
    
37
    
public boolean checkValid(String str){
38
        
synchronized(lock){
39
            System.out.println(ve.size());
40
            
return ve.contains(str);
41
        }

42
    }

43
}

說明:在reload和checkValid方法中都增加了synchronized關(guān)鍵字,對lock對象進(jìn)行加鎖。在不同線程中對同一個對象實例分別調(diào)用reload和checkValid方法。
在reload方法中,不修改lock對象即注釋lock="abc"; ,結(jié)果在控制臺輸出reload end后才輸出100。說明是同步調(diào)用的。
如果在reload方法中修改lock對象即去掉注釋,結(jié)果首先輸出了一個數(shù)字(當(dāng)前ve的大小),然后輸出reload end。說明是異步調(diào)用的。

2、單例模式中對多線程的考慮

 1
public class TestThread {
 2
     
private static final class TestThreadHolder {
 3
            
private static TestThread theSingleton = new TestThread();
 4
            
public static TestThread getSingleton() {
 5
                
return theSingleton;
 6
            }

 7
            
private TestThreadHolder() {
 8
            }

 9
        }

10
    
private Vector ve =null;
11
    
private Object lock=new Object();
12
    
private TestThread(){
13
        ve
=new Vector();
14
        initialize();
15
    }

16
    
public static TestThread getInstance(){
17
        
return TestThreadHolder.getSingleton();
18
    }

19
        
'''
20
}

說明:增加了一個內(nèi)部類,在內(nèi)部類中申明一個靜態(tài)的對象,實例化該單例類,初始化的數(shù)據(jù)都在單例類的構(gòu)造函數(shù)中進(jìn)行。這樣保證了多個實例同時訪問的時候,初始化的數(shù)據(jù)都已經(jīng)成功初始化了。

總結(jié):
A. 無論synchronized關(guān)鍵字加在方法上還是對象上,它取得的鎖都是對象,而不是把一段代碼或函數(shù)當(dāng)作鎖,所以首先應(yīng)知道需要加鎖的對象
B.每個對象只有一個鎖(lock)與之相關(guān)聯(lián)。
C.實現(xiàn)同步是要很大的系統(tǒng)開銷作為代價的,甚至可能造成死鎖,所以盡量避免無謂的同步控制。