深入研究Java的synchronized关键字(1)
1、写在类的成员方法上的synchronized关键字
运行以下代码:
public class Test { public static void main(String[] args) { Test x = new Test(); Thread t1 = new Thread(() -> x.a1()); Thread t2 = new Thread(() -> x.a2()); t1.start(); t2.start(); } private synchronized void a1() { try { Thread.sleep(2000); System.out.println("a1"); } catch (InterruptedException ex) {} } private void a2() { synchronized (this) { try { Thread.sleep(2000); System.out.println("a2"); } catch (InterruptedException ex) {} } } }
我们会发现"a1"和"a2"的打印时间总是相差2秒左右,从而可以推断:直接写在类的成员方法(例子中的a1)上的synchronized与synchronized(this)块其实是对同一个对象加锁。从而我们可以推断:直接写在类的成员方法上的synchronized,实际上相当于在方法体内的代码的最外层加了一个synchronized(this)块。
这样我们就容易理解为什么对于同一个对象,不能同时执行synchronized成员方法,但对于同一个类的两个不同对象,就可以同时执行synchronized成员方法了,因为对于同一个类的两个不同对象,它们的this的指向是不一样的。
执行以下代码:
public class Test { public static void main(String[] args) { Test x = new Test(), y = new Test(); Thread t1 = new Thread(() -> x.a1()); Thread t3 = new Thread(() -> y.a1()); t1.start(); t3.start(); } private synchronized void a1() { try { Thread.sleep(2000); System.out.println("a1"); } catch (InterruptedException ex) {} } }
会看到两个"a1"几乎同时打印出来,因为x.a1的执行不会阻塞y.a1的执行。
而执行以下代码:
public class Test { public static void main(String[] args) { Test x = new Test(); Thread t1 = new Thread(() -> x.a1()); Thread t4 = new Thread(() -> x.a4()); t1.start(); t4.start(); } private synchronized void a1() { try { Thread.sleep(2000); System.out.println("a1"); } catch (InterruptedException ex) {} } private synchronized void a4() { try { Thread.sleep(2000); System.out.println("a4"); } catch (InterruptedException ex) {} } }
可以看到"a1"和"a4"的打印时间总是相差2秒左右,因为执行a1和a4方法时都是对同一个对象x加锁。
2、写在类的静态方法上的synchronized关键字
运行以下代码:
public class Test { public static void main(String[] args) { Thread tt1 = new Thread(() -> Test.b1()); Thread tt2 = new Thread(() -> Test.b2()); tt1.start(); tt2.start(); } private static synchronized void b1() { try { Thread.sleep(2000); System.out.println("b1"); } catch (InterruptedException ex) {} } private static void b2() { synchronized (Test.class) { try { Thread.sleep(2000); System.out.println("b2"); } catch (InterruptedException ex) {} } } }
我们会发现"b1"和"b2"的打印时间总是相差2秒左右,从而可以推断:直接写在类的静态方法(例子中的a1)上的synchronized与synchronized(类对应的class对象)块其实是对同一个对象加锁。从而我们可以推断:直接写在类的静态方法上的synchronized,实际上相当于在方法体内的代码的最外层加了一个synchronized(类对应的class对象)块。
这样我们就容易理解为什么无论在类内还是类外,一个类的所有synchronized的静态方法总是不能同时执行。
运行以下代码:
public class Test { public static void main(String[] args) { Thread t01 = new Thread(() -> Test.b1()); Thread t02 = new Thread(() -> Test.b2()); Thread t03 = new Thread(() -> Test.B.b1()); Thread t04 = new Thread(() -> Test.B.b2()); t01.start(); t02.start(); t03.start(); t04.start(); } private static synchronized void b1() { try { Thread.sleep(2000); System.out.println("b1"); } catch (InterruptedException ex) {} } private static synchronized void b2() { try { Thread.sleep(2000); System.out.println("b2"); } catch (InterruptedException ex) {} } private static class B { static void b1() { Test.b1(); } static void b2() { Test.b2(); } } }
我们会发现每一行的打印都相隔2秒左右,因为不能两个线程同时执行Test.b1,也不能两个线程同时执行Test.b2,也不能同时一个线程执行Test.b1而另一个线程执行Test.b2。