原創, 轉載請注明出處!!     

        首先讓我們來看看下面的代碼
   
 1package net.blogjava.narry.stringlock;
 2
 3public class StringLockTest {
 4    
 5    
 6    /**
 7     * @param args
 8     */

 9    public static void main(String[] args) {
10        
11        Thread holdLockThread=new Thread(new Runnable(){
12
13            public void run() {
14                Test1 test1=new Test1();
15                test1.holdLock();
16            }

17            
18        }
);
19        holdLockThread.setName("holdLockThread");
20        
21        Thread printLockThread=new Thread(new Runnable(){
22
23            public void run() {
24                Test2 test2=new Test2();
25                test2.printHoldLock();
26            }

27            
28        }
);
29        printLockThread.setName("printLockThread");
30        holdLockThread.start();
31        try {
32            Thread.sleep(1000);
33        }
 catch (InterruptedException e) {
34            // TODO Auto-generated catch block
35            e.printStackTrace();
36        }

37        printLockThread.start();
38        
39    }

40    
41    private static class Test1{
42        
43        private String lock="stringLock";
44        public void holdLock(){
45            synchronized (lock) {
46                try {
47                    System.out.println("start sleep");
48                    Thread.sleep(1*60*60*1000);
49                }
 catch (InterruptedException e) {
50                    
51                    e.printStackTrace();
52                }

53            }

54        }

55    }

56    
57    private static class Test2{
58        private String lock="stringLock";
59        private void printHoldLock(){
60            synchronized (lock) {
61                System.out.println("get lock");
62            }

63        }

64    }

65    
66
67}

68
     乍眼一看,輸出的結果應該很簡單,應該輸出
      start sleep
      get lock
      現在,讓我們來看看,實際運行的結果又是如何呢?當運行這段代碼時,輸出如下圖所示:
      
      為什么沒有輸出get lock 呢?讓我們在來看看這個這兩個線程都在處理什么?打印線程堆棧如下:
    

 "printLockThread" prio=6 tid=0x02b13800 nid=0x284 waiting for monitor entry [0x02eaf000..0x02eafd14]
   java.lang.Thread.State: BLOCKED (on object monitor)
 at net.blogjava.narry.stringlock.StringLockTest$Test2.printHoldLock(StringLockTest.java:61)
 - waiting to lock <0x26a2b918> (a java.lang.String)
 at net.blogjava.narry.stringlock.StringLockTest$Test2.access$1(StringLockTest.java:59)
 at net.blogjava.narry.stringlock.StringLockTest$2.run(StringLockTest.java:25)
 at java.lang.Thread.run(Thread.java:619)

   Locked ownable synchronizers:
 - None

"holdLockThread" prio=6 tid=0x02b12800 nid=0x1d8 waiting on condition [0x02e5f000..0x02e5fd94]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
 at java.lang.Thread.sleep(Native Method)
 at net.blogjava.narry.stringlock.StringLockTest$Test1.holdLock(StringLockTest.java:48)
 - locked <0x26a2b918> (a java.lang.String)
 at net.blogjava.narry.stringlock.StringLockTest$1.run(StringLockTest.java:15)
 at java.lang.Thread.run(Thread.java:619)

   Locked ownable synchronizers:
 - None

         通過上面的線程堆文件,我們可以非常輕易的看到printLockThread 線程正在“waiting to lock <0x26a2b918>”,而這個鎖正好是線程holdLockThread所獲得的。
         如果將上面的代碼修改如下:

 1package net.blogjava.narry.stringlock;
 2
 3import com.sun.org.apache.bcel.internal.generic.NEW;
 4
 5public class StringLockTest {
 6    
 7    
 8    /**
 9     * @param args
10     */

11    public static void main(String[] args) {
12        
13        Thread holdLockThread=new Thread(new Runnable(){
14
15            public void run() {
16                Test1 test1=new Test1();
17                test1.holdLock();
18            }

19            
20        }
);
21        holdLockThread.setName("holdLockThread");
22        
23        Thread printLockThread=new Thread(new Runnable(){
24
25            public void run() {
26                Test2 test2=new Test2();
27                test2.printHoldLock();
28            }

29            
30        }
);
31        printLockThread.setName("printLockThread");
32        holdLockThread.start();
33        try {
34            Thread.sleep(1000);
35        }
 catch (InterruptedException e) {
36            // TODO Auto-generated catch block
37            e.printStackTrace();
38        }

39        printLockThread.start();
40        
41    }

42    
43    private static class Test1{
44        
45        private String lock=new String("stringLock");
46        public void holdLock(){
47            synchronized (lock) {
48                try {
49                    System.out.println("start sleep");
50                    Thread.sleep(1*60*60*1000);
51                }
 catch (InterruptedException e) {
52                    
53                    e.printStackTrace();
54                }

55            }

56        }

57    }

58    
59    private static class Test2{
60        private String lock="stringLock";
61        private void printHoldLock(){
62            synchronized (lock) {
63                System.out.println("get lock");
64            }

65        }

66    }

67    
68
69}

70
        運行上面的代碼,我們得到了如下的輸出:

        比較上面的兩段代碼的區別,僅僅在于在類Test1,將private String lock="stringLock"修改成了private String lock=new String("stringLock");
        通過上面的比較,其實反應了一個比較老的話題:String類的創建方式和在JVM中存儲的方式
        String對象的創建有兩種方式通過""和通過new String()來創建,通過雙引號來創建的String對象的信息存放在的Constant Pool中,我們將在Constant Pool中存放String的區域叫做String Pool,在運行期時,相同字符串的引用都指向String Pool中的相同的對象;而通過new方法創建的對象,會分別在heap上開辟存儲空間,作為不同的對象存在,說道這里大家可能已經清楚為什么會出現上面例子中的現象了。
       所以,如果采用字符串作為Lock,并且當創建的方式為“”時,就可能出現兩個方面的問題:
         1)增加了程序中不相關的部分的串行的運行的幾率,降低了并發度
         2)增加了程序中出現死鎖的可能性
        但是,這個特性在某個時候也能進行利用,因為通過String.intern方法我們可以獲得String對象在String Pool中所對應的對象或者是將自己加入到String Pool中,所以舉個例子,如果需要將日志記錄到不同的文件中,而且在每條日志的開始的部分有標識所要寫入日志的記錄文件的標志,因為寫入到相同文件的日志是要串行的,所以我們就可以通過使用每條日志中的記錄文件的標志的String.intern()為lock,實現在記錄到相同文件日志之間的串行記錄,不同文件的日志之間的并行記錄(注:此處只是舉例,不要在實際環境中使用,對于這種情況使用隊列應該更合適)。