synchronized修饰类中不同方法,调用的时候方法互斥吗

我们知道,使用synchronized关键字修饰方法时,多个线程调用此方法,会互斥执行。如果synchronized修饰不同的方法,多个线程再分别调用这些方法时是互斥的吗?下面使用代码模拟一下:

新建一个User类:

public class User {

    public synchronized void getUp() {
        System.out.println("起床...");
        try {
            TimeUnit.SECONDS.sleep(2L); // 暂停2s
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void eatBreakfast() {
        System.out.println("吃早餐...");
    }
}

User类中,两个方法都使用synchronized关键字修饰了,写一个测试方法测试一下:

注意:Junit单元测试时,当主线程结束后,不管子线程是否结束都会退出,所以这里使用CountDownLatch,让主线阻塞等待子线程运行完成。

	@Test
    public void test1() {
        CountDownLatch latch = new CountDownLatch(2);
        User user = new User(); // 创建user对象
        User user2 = new User(); // 创建user2对象
        new Thread(() -> {
            user.getUp();
            latch.countDown();
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            user2.eatBreakfast();
            latch.countDown();
        }).start();
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

运行结果:

起床...
吃早餐...

可以看到,创建两个对象调用不同的synchronized修饰的方法的时候,并没有出现互斥访问的情况,eatBreakfast方法并没有等待getUp方法执行完毕再执行,原因也很简单,这两个方法获取的不是同一个锁对象。

修改测试方法,改成一个对象来访问不同的synchronized修饰的方法:

	@Test
    public void test2() {
        CountDownLatch latch = new CountDownLatch(2);
        User user = new User(); // 创建user对象
        new Thread(() -> {
            user.getUp();
            latch.countDown();
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            user.eatBreakfast();
            latch.countDown();
        }).start();
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

运行结果:

起床...
吃早餐...

可以看到,方法是顺序执行的,也就是互斥访问的,getUp方法中,使用TimeUnit.SECONDS.sleep(2L)让方法延迟了2s,然后在测试方法中延迟1s调用eatBreakfast方法,如果方法不是互斥访问的,应该是eatBreakfast先执行,为了看清效果,你可以让getUp方法延迟更久,看一下是不是还是要等getUp方法执行完毕再执行eatBreakfast方法。

这说明,synchronized锁的对象是调用者,这两个方法用的是同一把锁,谁先拿到,谁先执行,执行完毕释放锁以后,下一个方法获取锁才执行,而不是谁先被调用就一定先被执行。同理static synchronized修饰的静态同步方法也是类似的,由于锁是字节码文件对象,调用是也是互斥的,谁先获取锁谁就先被执行。

posted @ 2020-05-22 11:49  三分魔系  阅读(113)  评论(0编辑  收藏  举报