写个demo测试一下:
public class A { public static void main(String[] args) { A obj_a = new A(); new Thread() { @Override public void run() { try { obj_a.a(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); new Thread() { @Override public void run() { try { obj_a.b(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); new Thread(){ @Override public void run() { try { A.c(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); try { A.d(); } catch (InterruptedException e) { e.printStackTrace(); } } public synchronized void a() throws InterruptedException { System.out.println("--- begin a - current Thread " + Thread.currentThread().getId()); Thread.sleep(8000); System.out.println("--- end a - current Thread " + Thread.currentThread().getId()); } public synchronized void b() throws InterruptedException { System.out.println("--- begin b - current Thread " + Thread.currentThread().getId()); Thread.sleep(8000); System.out.println("--- end b - current Thread " + Thread.currentThread().getId()); } public synchronized static void c() throws InterruptedException { System.out.println("--- begin c - current Thread " + Thread.currentThread().getId()); Thread.sleep(5000); System.out.println("--- end c - current Thread " + Thread.currentThread().getId()); } public synchronized static void d() throws InterruptedException { System.out.println("--- begin d - current Thread " + Thread.currentThread().getId()); Thread.sleep(5000); System.out.println("--- end d - current Thread " + Thread.currentThread().getId()); } }运行结果如下:
可以看到,由于方法 a 和 方法 b 是同一个锁 obj_A,所以当某个线程执行其中一个方法是,其他线程也不能执行另一个方法。但是方法 c 是由 A.class 对象锁住的,执行方法 C 的线程与另外两个线程没有互斥关系。
对于某个类的某个特定对象来说,该类中,所有 synchronized 修饰的非静态方法共享同一个锁,当在对象上调用其任意 synchronized 方法时,此对象都被加锁,此时,其他线程调用该对象上任意的 synchronized 方法只有等到前一个线程方法调用完毕并释放了锁之后才能被调用。
而对于一个类中,所有 synchronized 修饰的静态方法共享同一个锁。
当某个线程请求一个由其他线程持有的锁时,发出请求的线程就会阻塞,然而,内置锁是可重入的,,如果某个线程试图获得一个已经由它自己持有的锁,那么这个请求就会成功。这也意味着获取锁的操作粒度是“线程”,而不是“调用”。当线程请求一个未被持有的锁时,jvm 将记下锁的持有者(即哪个线程),并获取该锁的计数值为 1 ,当同一个线程再次获取该锁时, jvm 将计数值递增,当线程退出同步代码块时,计数值将递减,当计数值为 0 时,将释放锁。
public class B { public static void main(String[] args) { B obj_B = new B(); obj_B.b(); } private synchronized void a(){ System.out.println("---a"); } private synchronized void b(){ System.out.println("---b"); a(); } }执行上面这段代码,将输出
---b ---a假设没有可重入的锁,对于对象 obj_B 来说,调用 b 方法时,线程将会持有 obj_B 这个锁,在方法 b 中调用方法 a 时,将会一直等待方法 b 释放锁,造成死锁的情况。
《Java 并发编程实战》 中举的可重入锁的例子:
我第一遍看这段代码的时候,在思考,这里父类子类的方法都有synchronized同步,当调用子类LoggingWidget的doSomething()时锁对象肯定是当时调用的那个LoggingWidget实例,可是问题是当执行到super.doSomething()时,要调用父类的同步方法,那此时锁对象是谁?是同一个锁进入了 2 次,还是获得了子类对象和父类对象的 2 个不同的锁?
下面这段代码能给出结论:
这段代码输出了:
something sleepy! woke up! something else而不是
something sleepy! something else woke up!