原創(chuàng), 轉(zhuǎn)載請(qǐng)注明出處!!     

        首先讓我們來看看下面的代碼
   
 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
     乍眼一看,輸出的結(jié)果應(yīng)該很簡(jiǎn)單,應(yīng)該輸出
      start sleep
      get lock
      現(xiàn)在,讓我們來看看,實(shí)際運(yùn)行的結(jié)果又是如何呢?當(dāng)運(yùn)行這段代碼時(shí),輸出如下圖所示:
      
      為什么沒有輸出get lock 呢?讓我們?cè)趤砜纯催@個(gè)這兩個(gè)線程都在處理什么?打印線程堆棧如下:
    

 "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>”,而這個(gè)鎖正好是線程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
        運(yùn)行上面的代碼,我們得到了如下的輸出:

        比較上面的兩段代碼的區(qū)別,僅僅在于在類Test1,將private String lock="stringLock"修改成了private String lock=new String("stringLock");
        通過上面的比較,其實(shí)反應(yīng)了一個(gè)比較老的話題:String類的創(chuàng)建方式和在JVM中存儲(chǔ)的方式
        String對(duì)象的創(chuàng)建有兩種方式通過""和通過new String()來創(chuàng)建,通過雙引號(hào)來創(chuàng)建的String對(duì)象的信息存放在的Constant Pool中,我們將在Constant Pool中存放String的區(qū)域叫做String Pool,在運(yùn)行期時(shí),相同字符串的引用都指向String Pool中的相同的對(duì)象;而通過new方法創(chuàng)建的對(duì)象,會(huì)分別在heap上開辟存儲(chǔ)空間,作為不同的對(duì)象存在,說道這里大家可能已經(jīng)清楚為什么會(huì)出現(xiàn)上面例子中的現(xiàn)象了。
       所以,如果采用字符串作為L(zhǎng)ock,并且當(dāng)創(chuàng)建的方式為“”時(shí),就可能出現(xiàn)兩個(gè)方面的問題:
         1)增加了程序中不相關(guān)的部分的串行的運(yùn)行的幾率,降低了并發(fā)度
         2)增加了程序中出現(xiàn)死鎖的可能性
        但是,這個(gè)特性在某個(gè)時(shí)候也能進(jìn)行利用,因?yàn)橥ㄟ^String.intern方法我們可以獲得String對(duì)象在String Pool中所對(duì)應(yīng)的對(duì)象或者是將自己加入到String Pool中,所以舉個(gè)例子,如果需要將日志記錄到不同的文件中,而且在每條日志的開始的部分有標(biāo)識(shí)所要寫入日志的記錄文件的標(biāo)志,因?yàn)閷懭氲较嗤募娜罩臼且械模晕覀兙涂梢酝ㄟ^使用每條日志中的記錄文件的標(biāo)志的String.intern()為lock,實(shí)現(xiàn)在記錄到相同文件日志之間的串行記錄,不同文件的日志之間的并行記錄(注:此處只是舉例,不要在實(shí)際環(huán)境中使用,對(duì)于這種情況使用隊(duì)列應(yīng)該更合適)。