synchronized的作用
一、同步方法
public synchronized void methodAAA(){
//….
}
鎖定的是調(diào)用這個(gè)同步方法的對(duì)象
測(cè)試:
a、不使用這個(gè)關(guān)鍵字修飾方法,兩個(gè)線程調(diào)用同一個(gè)對(duì)象的這個(gè)方法。
目標(biāo)類:
1public class TestThread {
2 public void execute(){ //synchronized,未修飾
3 for(int i=0;i<100;i++){
4 System.out.println(i);
5 }
6 }
7}
線程類:
1public class ThreadA implements Runnable{
2 TestThread test=null;
3 public ThreadA(TestThread pTest){ //對(duì)象有外部引入,這樣保證是同一個(gè)對(duì)象
4 test=pTest;
5 }
6 public void run() {
7 test.execute();
8 }
9} 調(diào)用:
1TestThread test=new TestThread();
2Runnable runabble=new ThreadA(test);
3Thread a=new Thread(runabble,"A");
4a.start();
5Thread b=new Thread(runabble,"B");
6b.start();
結(jié)果:
輸出的數(shù)字交錯(cuò)在一起。說(shuō)明不是同步的,兩個(gè)方法在不同的線程中是異步調(diào)用的。
b、修改目標(biāo)類,增加synchronized修飾
1public 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,說(shuō)明是同步的,雖然是不同的線程,但兩個(gè)方法是同步調(diào)用的。
注意:上面雖然是兩個(gè)不同的線程,但是是同一個(gè)實(shí)例對(duì)象。下面使用不同的實(shí)例對(duì)象進(jìn)行測(cè)試。
c、每個(gè)線程都有獨(dú)立的TestThread對(duì)象。
目標(biāo)類:
1public class TestThread {
2 public synchronized void execute(){ //synchronized修飾
3 for(int i=0;i<100;i++){
4 System.out.println(i);
5 }
6 }
7} 線程類:
1public class ThreadA implements Runnable{
2 public void run() {
3 TestThread test=new TestThread();
4 test.execute();
5 }
6}
7 調(diào)用:
1Runnable runabble=new ThreadA();
2Thread a=new Thread(runabble,"A");
3a.start();
4Thread b=new Thread(runabble,"B");
5b.start();
結(jié)果:
輸出的數(shù)字交錯(cuò)在一起。說(shuō)明雖然增加了synchronized 關(guān)鍵字來(lái)修飾方法,但是不同的線程調(diào)用各自的對(duì)象實(shí)例,兩個(gè)方法仍然是異步的。
引申:
對(duì)于這種多個(gè)實(shí)例,要想實(shí)現(xiàn)同步即輸出的數(shù)字是有序并且按線程先后順序輸出,我們可以增加一個(gè)靜態(tài)變量,對(duì)它進(jìn)行加鎖(后面將說(shuō)明鎖定的對(duì)象)。
修改目標(biāo)類:
1public 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} 二、同步代碼塊
1public void method(SomeObject so){
2 synchronized(so)
3 //…..
4 }
5} 鎖定一個(gè)對(duì)象,其實(shí)鎖定的是該對(duì)象的引用(object reference)
誰(shuí)拿到這個(gè)鎖誰(shuí)就可以運(yùn)行它所控制的那段代碼。當(dāng)有一個(gè)明確的對(duì)象作為鎖時(shí),就可以按上面的代碼寫(xiě)程序,但當(dāng)沒(méi)有明確的對(duì)象作為鎖,只是想讓一段代碼同步時(shí),可以創(chuàng)建一個(gè)特殊的instance變量(它必須是一個(gè)對(duì)象)來(lái)充當(dāng)鎖(上面的解決方法就是增加了一個(gè)狀態(tài)鎖)。
a、鎖定一個(gè)對(duì)象,它不是靜態(tài)的
private byte[] lock = new byte[0]; // 特殊的instance變量
目標(biāo)類:
1public class TestThread {
2 private Object lock=new Object();
3 public void execute(){
4 synchronized(lock){ //增加了個(gè)鎖,鎖定了對(duì)象lock,在同一個(gè)類實(shí)例中,是線程安全的,但不同的實(shí)例還是不安全的。
5
6因?yàn)椴煌膶?shí)例有不同對(duì)象鎖lock
7 for(int i=0;i<100;i++){
8 System.out.println(i);
9 }
10 }
11 }
12}
其實(shí)上面鎖定一個(gè)方法,等同于下面的:
1public void execute(){
2 synchronized(this){ //同步的是當(dāng)然對(duì)象
3 for(int i=0;i<100;i++){
4 System.out.println(i);
5 }
6 }
7} b、鎖定一個(gè)對(duì)象或方法,它是靜態(tài)的
這樣鎖定,它鎖定的是對(duì)象所屬的類
public synchronized static void execute(){
//...
}
等同于
1public class TestThread {
2 public static void execute(){
3 synchronized(TestThread.class){
4 //
5 }
6 }
7} 測(cè)試:
目標(biāo)類:
1public 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(); //輸出是有序的,說(shuō)明是同步的
15 //execute1(); //輸出是無(wú)須的,說(shuō)明是異步的
16 }
17} 線程類:調(diào)用不同的方法,于是建立了兩個(gè)線程類
1public class ThreadA implements Runnable{
2 public void run() {
3 TestThread.execute();//調(diào)用同步靜態(tài)方法
4 }
5}
6public class ThreadB implements Runnable{
7 public void run() {
8 TestThread test=new TestThread();
9 test.test();//調(diào)用非同步非靜態(tài)方法
10 }
11} 調(diào)用:
1Runnable runabbleA=new ThreadA();
2Thread a=new Thread(runabbleA,"A");
3a.start();
4Runnable runabbleB=new ThreadB();
5Thread b=new Thread(runabbleB,"B");
6b.start(); 注意:
1、用synchronized 來(lái)鎖定一個(gè)對(duì)象的時(shí)候,如果這個(gè)對(duì)象在鎖定代碼段中被修改了,則這個(gè)鎖也就消失了。看下面的實(shí)例:
目標(biāo)類:
1public 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} 說(shuō)明:在reload和checkValid方法中都增加了synchronized關(guān)鍵字,對(duì)lock對(duì)象進(jìn)行加鎖。在不同線程中對(duì)同一個(gè)對(duì)象實(shí)例分別調(diào)用reload和checkValid方法。
在reload方法中,不修改lock對(duì)象即注釋lock="abc"; ,結(jié)果在控制臺(tái)輸出reload end后才輸出100。說(shuō)明是同步調(diào)用的。
如果在reload方法中修改lock對(duì)象即去掉注釋,結(jié)果首先輸出了一個(gè)數(shù)字(當(dāng)前ve的大小),然后輸出reload end。說(shuō)明是異步調(diào)用的。
2、單例模式中對(duì)多線程的考慮
1public 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} 說(shuō)明:增加了一個(gè)內(nèi)部類,在內(nèi)部類中申明一個(gè)靜態(tài)的對(duì)象,實(shí)例化該單例類,初始化的數(shù)據(jù)都在單例類的構(gòu)造函數(shù)中進(jìn)行。這樣保證了多個(gè)實(shí)例同時(shí)訪問(wèn)的時(shí)候,初始化的數(shù)據(jù)都已經(jīng)成功初始化了。
總結(jié):
A. 無(wú)論synchronized關(guān)鍵字加在方法上還是對(duì)象上,它取得的鎖都是對(duì)象,而不是把一段代碼或函數(shù)當(dāng)作鎖,所以首先應(yīng)知道需要加鎖的對(duì)象
B.每個(gè)對(duì)象只有一個(gè)鎖(lock)與之相關(guān)聯(lián)。
C.實(shí)現(xiàn)同步是要很大的系統(tǒng)開(kāi)銷作為代價(jià)的,甚至可能造成死鎖,所以盡量避免無(wú)謂的同步控制。