synchronized的作用
一、同步方法
public synchronized void methodAAA(){
//….
}
鎖定的是調用這個同步方法的對象
測試:
a、不使用這個關鍵字修飾方法,兩個線程調用同一個對象的這個方法。
目標類:

public class TestThread
{2

public void execute()
{ //synchronized,未修飾3

for(int i=0;i<100;i++)
{4
System.out.println(i);5
} 6
}7
}
線程類:

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
}調用:
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();
結果:
輸出的數(shù)字交錯在一起。說明不是同步的,兩個方法在不同的線程中是異步調用的。
b、修改目標類,增加synchronized修飾

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
}
結果:
輸出的數(shù)字是有序的,首先輸出A的數(shù)字,然后是B,說明是同步的,雖然是不同的線程,但兩個方法是同步調用的。
注意:上面雖然是兩個不同的線程,但是是同一個實例對象。下面使用不同的實例對象進行測試。
c、每個線程都有獨立的TestThread對象。
目標類:

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
}線程類:

public class ThreadA implements Runnable
{2

public void run()
{3
TestThread test=new TestThread();4
test.execute();5
}6
}7

調用:
Runnable runabble=new ThreadA();2
Thread a=new Thread(runabble,"A"); 3
a.start();4
Thread b=new Thread(runabble,"B");5
b.start();
結果:
輸出的數(shù)字交錯在一起。說明雖然增加了synchronized 關鍵字來修飾方法,但是不同的線程調用各自的對象實例,兩個方法仍然是異步的。
引申:
對于這種多個實例,要想實現(xiàn)同步即輸出的數(shù)字是有序并且按線程先后順序輸出,我們可以增加一個靜態(tài)變量,對它進行加鎖(后面將說明鎖定的對象)。
修改目標類:

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
}二、同步代碼塊

public void method(SomeObject so)
{2
synchronized(so)3
//…..4
}5
}鎖定一個對象,其實鎖定的是該對象的引用(object reference)
誰拿到這個鎖誰就可以運行它所控制的那段代碼。當有一個明確的對象作為鎖時,就可以按上面的代碼寫程序,但當沒有明確的對象作為鎖,只是想讓一段代碼同步時,可以創(chuàng)建一個特殊的instance變量(它必須是一個對象)來充當鎖(上面的解決方法就是增加了一個狀態(tài)鎖)。
a、鎖定一個對象,它不是靜態(tài)的
private byte[] lock = new byte[0]; // 特殊的instance變量
目標類:

public class TestThread
{2
private Object lock=new Object(); 3

public void execute()
{4

synchronized(lock)
{ //增加了個鎖,鎖定了對象lock,在同一個類實例中,是線程安全的,但不同的實例還是不安全的。5

6
因為不同的實例有不同對象鎖lock7

for(int i=0;i<100;i++)
{8
System.out.println(i);9
} 10
}11
}12
}
其實上面鎖定一個方法,等同于下面的:

public void execute()
{ 2

synchronized(this)
{ //同步的是當然對象3

for(int i=0;i<100;i++)
{4
System.out.println(i);5
} 6
}7
}b、鎖定一個對象或方法,它是靜態(tài)的
這樣鎖定,它鎖定的是對象所屬的類
public synchronized static void execute(){
//...
}
等同于

public class TestThread
{2

public static void execute()
{3

synchronized(TestThread.class)
{4
//
5
}6
}7
}測試:
目標類:

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
}線程類:調用不同的方法,于是建立了兩個線程類

public class ThreadA implements Runnable
{2

public void run()
{3
TestThread.execute();//調用同步靜態(tài)方法4
}5
}6

public class ThreadB implements Runnable
{7

public void run()
{8
TestThread test=new TestThread();9
test.test();//調用非同步非靜態(tài)方法10
}11
}調用:
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>
目標類:

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關鍵字,對lock對象進行加鎖。在不同線程中對同一個對象實例分別調用reload和checkValid方法。
在reload方法中,不修改lock對象即注釋lock="abc"; ,結果在控制臺輸出reload end后才輸出100。說明是同步調用的。
如果在reload方法中修改lock對象即去掉注釋,結果首先輸出了一個數(shù)字(當前ve的大小),然后輸出reload end。說明是異步調用的。
2、單例模式中對多線程的考慮

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
}說明:增加了一個內部類,在內部類中申明一個靜態(tài)的對象,實例化該單例類,初始化的數(shù)據(jù)都在單例類的構造函數(shù)中進行。這樣保證了多個實例同時訪問的時候,初始化的數(shù)據(jù)都已經(jīng)成功初始化了。
總結:
A. 無論synchronized關鍵字加在方法上還是對象上,它取得的鎖都是對象,而不是把一段代碼或函數(shù)當作鎖,所以首先應知道需要加鎖的對象
B.每個對象只有一個鎖(lock)與之相關聯(lián)。
C.實現(xiàn)同步是要很大的系統(tǒng)開銷作為代價的,甚至可能造成死鎖,所以盡量避免無謂的同步控制。




