起因
最近上网突然看到别人提的一个问题,感觉蛮有趣的,自己重来没有想过.
把它抽象出来就是:如果我再synchronized中锁住了一个对象,然后在同步块中修改了指向这个对象的引用会怎么样?
实验
1 public class SynchronizedTest1 implements Runnable { 2 public static Object lock = new Object(); 3 4 @Override 5 public void run() { 6 synchronized (lock) { 7 System.out.println(Thread.currentThread().getName() + lock); 8 lock = new Object(); 9 try { 10 Thread.sleep(1000); 11 } catch (InterruptedException e) { 12 // TODO Auto-generated catch block 13 e.printStackTrace(); 14 } 15 System.out.println(Thread.currentThread().getName() + lock); 16 } 17 } 18 19 public static void main(String[] args) { 20 Thread t1 = new Thread(new SynchronizedTest1()); 21 Thread t2 = new Thread(new SynchronizedTest1()); 22 t1.start(); 23 t2.start(); 24 } 25 26 }
可能的输出:
Thread-0java.lang.Object@153bcbc8
Thread-1java.lang.Object@8bfc25c
Thread-0java.lang.Object@65712a80
Thread-1java.lang.Object@65712a80
这说明在线程0做完lock=new Object()的时候,线程1可以立刻进入同步块.
另外1个例子:
1 public class SynchronizedTest2 implements Runnable { 2 public static String lock = "123"; 3 4 @Override 5 public void run() { 6 synchronized (lock) { 7 System.out.println(Thread.currentThread().getName() + lock); 8 lock = "123"; 9 try { 10 Thread.sleep(1000); 11 } catch (InterruptedException e) { 12 // TODO Auto-generated catch block 13 e.printStackTrace(); 14 } 15 System.out.println(Thread.currentThread().getName() + lock); 16 } 17 } 18 19 public static void main(String[] args) { 20 Thread t1 = new Thread(new SynchronizedTest2()); 21 Thread t2 = new Thread(new SynchronizedTest2()); 22 t1.start(); 23 t2.start(); 24 } 25 26 }
输出:
Thread-0123
Thread-0123
Thread-1123
Thread-1123
线程0和线程1不会交叉输出.
结论
2个例子的差别就是第二个例子里因为有常量池的原因,lock永远指向的是同一个String对象,所以虽然在线程0中修改了lock的指向,但是还是指向了同一个String对象,所以线程1还是在synchronized那里阻塞.
而在例子一中,当lock=new Object()的时候线程1可以即可进入synchronized,因为这个时候线程1synchronzed的对象已经和线程0synchronized的对象不同了.
这2个例子说明锁是锁在对象上的,而不是引用上的.