深入研究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。

posted @ 2018-09-26 10:13  Firas  阅读(253)  评论(0编辑  收藏  举报