synchronized锁住的是什么

360 阅读3分钟

1. synchronized加在普通方法上

public synchronized void method1() {
    System.out.println(Thread.currentThread().getName() + "访问了同步方法1");
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

public synchronized void method2() {
    System.out.println(Thread.currentThread().getName() + "访问了同步方法2");
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

1.1 通过同一个对象引用去调用

SynchronizedDemo demo1 = new SynchronizedDemo();
Thread t1 = new Thread(() -> demo1.method1());
Thread t2 = new Thread(() -> demo1.method2());
t1.start();
t2.start();

测试结果:会阻塞,因为synchronized加在方法上锁住的是当前类的引用对象

1.2 通过不同对象引用去调用

SynchronizedDemo demo1 = new SynchronizedDemo();
SynchronizedDemo demo2 = new SynchronizedDemo();
Thread t1 = new Thread(() -> demo1.method1());
Thread t2 = new Thread(() -> demo2.method2());
t1.start();
t2.start();

测试结果:不会阻塞,因为锁住的是当前类的引用对象,而调用使用的是不同的对象

2. synchronized加在静态方法上

public static synchronized void method1() {
    System.out.println(Thread.currentThread().getName() + "访问了同步方法1");
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

public static synchronized void method2() {
    System.out.println(Thread.currentThread().getName() + "访问了同步方法2");
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

2.1 通过同一个对象去调用

SynchronizedDemo demo1 = new SynchronizedDemo();
Thread t1 = new Thread(() -> demo1.method1());
Thread t2 = new Thread(() -> demo1.method2());
t1.start();
t2.start();

测试结果:会阻塞,因为方法被static修饰,表示该方法属于类,而Class只有一份,所以锁住的是同一个对象

2.2 通过不同对象去调用

SynchronizedDemo demo1 = new SynchronizedDemo();
SynchronizedDemo demo2 = new SynchronizedDemo();
Thread t1 = new Thread(() -> demo1.method1());
Thread t2 = new Thread(() -> demo2.method2());
t1.start();
t2.start();

测试结果:会阻塞,原因同上

3. 同步代码块锁住this

public void method3() {
    synchronized (this) {
        System.out.println(Thread.currentThread().getName() + "访问了this的同步代码块3");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public void method4() {
    synchronized (this) {
        System.out.println(Thread.currentThread().getName() + "访问了this的同步代码块4");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

3.1 通过同一个对象去调用

SynchronizedDemo demo1 = new SynchronizedDemo();
Thread t1 = new Thread(() -> demo1.method3());
Thread t2 = new Thread(() -> demo1.method4());
t1.start();
t2.start();

测试结果:会阻塞,因为this为对象实例,所以锁住的是同一个对象

3.2 通过不同对象去调用

SynchronizedDemo demo1 = new SynchronizedDemo();
SynchronizedDemo demo2 = new SynchronizedDemo();
Thread t1 = new Thread(() -> demo1.method3());
Thread t2 = new Thread(() -> demo2.method4());
t1.start();
t2.start();

测试结果:不会阻塞,因为使用的是两个不同的对象实例

总结:synchronized加在普通方法上和锁住this作用相同,都是锁当前对象实例

4. 同步代码块锁住不同的Object

Object lock1 = new Object();
Object lock2 = new Object();

public void method5() {
    synchronized (lock1) {
        System.out.println(Thread.currentThread().getName() + "访问了lock1的同步代码块5");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public void method6(){
    synchronized (lock2) {
        System.out.println(Thread.currentThread().getName() + "访问了lock2的同步代码块6");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

4.1 通过相同的对象去调用

SynchronizedDemo demo1 = new SynchronizedDemo();
Thread t1 = new Thread(() -> demo1.method5());
Thread t2 = new Thread(() -> demo1.method6());
t1.start();
t2.start();

测试结果:不会阻塞,因为锁住的是同一引用下的不同对象实例

5. 同步代码块锁住相同的Object

public void method5() {
    synchronized (lock1) {
        System.out.println(Thread.currentThread().getName() + "访问了lock1的同步代码块5");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public void method6(){
    synchronized (lock1) {
        System.out.println(Thread.currentThread().getName() + "访问了lock1的同步代码块6");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

5.1 使用相同对象去调用

SynchronizedDemo demo1 = new SynchronizedDemo();
Thread t1 = new Thread(() -> demo1.method5());
Thread t2 = new Thread(() -> demo1.method6());
t1.start();
t2.start();

测试结果:会阻塞,因为锁住的是同一引用下的相同对象实例,如果锁住不同对象或使用不同对象实例去调用则不会阻塞

6. 同步代码块锁住类对象

public void method7() {
    synchronized (SynchronizedDemo.class) {
        System.out.println(Thread.currentThread().getName() + "访问了Class的同步代码块5");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public void method8(){
    synchronized (SynchronizedDemo.class) {
        System.out.println(Thread.currentThread().getName() + "访问了Class的同步代码块6");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

6.1 使用不同对象去调用

SynchronizedDemo demo1 = new SynchronizedDemo();
SynchronizedDemo demo2 = new SynchronizedDemo();
Thread t1 = new Thread(() -> demo1.method7());
Thread t2 = new Thread(() -> demo2.method8());
t1.start();
t2.start();

测试结果:会阻塞,由于锁住的是Xxx.class所以锁住的是Class对象,这和被标记为static的方法锁住的都是同一个对象,所以无论使用相同引用还是不同引用都会被阻塞住,因为Class只有一个。