原创, 转载请注明出处!!     

        首先让我们来看看下面的代码
   
 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,实现在记录到相同文件日志之间的串行记录,不同文件的日志之间的并行记录(注:此处只是举例,不要在实际环境中使用,对于这种情况使用队列应该更合适)。