八锁现象彻底理解锁

HeJin大约 4 分钟JavaJUC并发编程

锁是什么?如何判断锁的是谁?深刻理解锁。

关于锁的8个问题。

1、标准情况下,两个线程谁先打印

public class Test1 {
    public static void main(String[] args) {
        Phone phone = new Phone();

        new Thread(()->{
            phone.sendSms();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone.call();
        },"B").start();
    }
}

class Phone{
    public synchronized void sendSms(){
        System.out.println("发短信");
    }
    public synchronized void call(){
        System.out.println("打电话");
    }
}
发短信
打电话

Process finished with exit code 0

被synchronized修饰的方法 锁的对象是方法的调用者。两个方法用的是同一个锁谁先拿到谁执行

2、sendSms延迟4秒

public class Test1 {
    public static void main(String[] args) {
        Phone phone = new Phone();

        new Thread(()->{
            phone.sendSms();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone.call();
        },"B").start();
    }
}

class Phone{
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    
    public synchronized void call(){
        System.out.println("打电话");
    }
}
发短信
打电话

Process finished with exit code 0

原因

被synchronized修饰的方法 锁的对象是方法的调用者。两个方法用的是同一个锁谁先拿到谁执行

因为在main方法中调用了休眠,位于休眠后的方法先执行的概率远远低于位于休眠前的方法。

3、增加了一个普通方法

public class Test2 {
    public static void main(String[] args) {
        Phone2 phone = new Phone2();

        new Thread(()->{
            phone.sendSms();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone.hello();
        },"B").start();
    }
}

class Phone2{
    /**
     * 被synchronized修饰的方法 锁的对象是方法的调用者
     * 两个方法用的是同一个锁,谁先拿到谁执行
     */
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call(){
        System.out.println("打电话");
    }

    /**
     * 这里没有锁!不是同步方法,不受锁的影响
     */
    public void hello(){
        System.out.println("hello");
    }
}
hello
发短信

Process finished with exit code 0

普通方法没有锁!不是同步方法,不受锁的影响。

4、两个对象,两个同步方法

public class Test2 {
    public static void main(String[] args) {
        // 两个对象,两个调用者,两把锁
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();

        new Thread(()->{
            phone1.sendSms();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}

class Phone2{
    /**
     * 被synchronized修饰的方法 锁的对象是方法的调用者
     * 两个方法用的是同一个锁,谁先拿到谁执行
     */
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call(){
        System.out.println("打电话");
    }

    /**
     * 这里没有锁!不是同步方法,不受锁得影响
     */
    public void hello(){
        System.out.println("hello");
    }
}
打电话
发短信

Process finished with exit code 0

原因:两个对象,两个调用者,两把锁。发短信有4秒的延迟。

5、增加两个静态的同步方法,只有一个对象

public class Test3 {
    public static void main(String[] args) {
        // 两个对象,两个调用者,两把锁
        Phone3 phone = new Phone3();

        new Thread(()->{
            phone.sendSms();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone.call();
        },"B").start();
    }
}

class Phone3{
    /**
     * 被synchronized修饰的方法 锁的对象是方法的调用者
     * 两个方法用的是同一个锁,谁先拿到谁执行
     */
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public static synchronized void call(){
        System.out.println("打电话");
    }
}
发短信
打电话

Process finished with exit code 0

原因:static静态方法,类一加载就有了,全局唯一。锁的是Class 。

6、两个对象,两个静态同步方法

public class Test3 {
    public static void main(String[] args) {
        // 两个对象,两个调用者,两把锁
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();

        new Thread(()->{
            phone1.sendSms();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}

class Phone3{
    /**
     * 被synchronized修饰的方法 锁的对象是方法的调用者
     * 两个方法用的是同一个锁,谁先拿到谁执行
     */
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public static synchronized void call(){
        System.out.println("打电话");
    }
}
发短信
打电话

Process finished with exit code 0

原因:static静态方法,类一加载就有了,全局唯一。锁的是Class 。

7、一个静态同步方法,一个普通静态方法,一个对象

public class Test4 {
    public static void main(String[] args) {

        Phone4 phone = new Phone4();

        new Thread(()->{
            phone.sendSms();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone.call();
        },"B").start();
    }
}

class Phone4{
    /**
     * 静态同步方法
     */
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    /**
     * 普通的同步方法
     */
    public synchronized void call(){
        System.out.println("打电话");
    }
}
打电话
发短信

Process finished with exit code 0

原因:静态同步方法。锁的是Class类模板。普通的同步方法,锁的是调用者。

8、一个静态同步方法,一个普通静态方法,两个对象

public class Test4 {
    public static void main(String[] args) {

        Phone4 phone1 = new Phone4();
        Phone4 phone2 = new Phone4();

        new Thread(()->{
            phone1.sendSms();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}

class Phone4{
    /**
     * 静态同步方法。锁的是Class类模板
     */
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    /**
     * 普通的同步方法,锁的是调用者。
     */
    public synchronized void call(){
        System.out.println("打电话");
    }
}
打电话
发短信

Process finished with exit code 0

原因:静态同步方法。锁的是Class类模板。普通的同步方法,锁的是调用者。

小结

new this 具体的对象。static Class 唯一的一个模板。