Java 关于锁的灵魂8问,彻底理解锁

1、直接看代码,线程A和线程B谁先执行?

public class Lock {
    
    

    public static void main(String[] args) {
    
    

        Phone phone = new Phone();
        
        new Thread(() -> {
    
    
            phone.message();
        }, "线程A").start();
        
        new Thread(() -> {
    
    
            phone.call();
        }, "线程B").start();
    }
    
}
class Phone {
    
    

    public synchronized void message() {
    
    
        System.out.println("发消息");
    }

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

}

答案:先执行线程A,再执行线程B

1、class Phone{}中的两个方法都是用synchronized 修饰的,synchronized锁的对象是方法的调用者
2、main()方法中的两个线程用的都是同一个Phone对象,所以他们也就是共用的一把锁
3、既然是共用一把锁,肯定就是谁先拿到谁先执行


2、线程A睡眠4S,谁先执行?

public class Lock8 {
    
    

    public static void main(String[] args) {
    
    

        Phone phone = new Phone();

        new Thread(() -> {
    
    
            phone.message();
        }, "线程A").start();

        new Thread(() -> {
    
    
            phone.call();
        }, "线程B").start();

    }

}

class Phone {
    
    

    /**
     * 发短信的线程 睡眠四秒
     */
    public synchronized void message() {
    
    
        try {
    
    
            TimeUnit.SECONDS.sleep(4);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        System.out.println("发消息");
    }

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

}

答案:先执行线程A,再执行线程B

1、再次证明上一问的答案,两个线程是共用的一把锁
2、不管线程A睡眠多久,线程B都不可能先执行,因为锁被线程A占用着


3、线程A还是睡眠4S,增加普通方法hello(),谁先执行?

public class Lock {
    
    

    public static void main(String[] args) {
    
    

        Phone phone = new Phone();

        new Thread(() -> {
    
    
            phone.message();
        }, "线程A").start();


        new Thread(() -> {
    
    
            phone.hello();
        }, "线程B").start();

    }

}

class Phone {
    
    

    /**
     * 发短信的线程 睡眠四秒
     */
    public synchronized void message() {
    
    
        try {
    
    
            TimeUnit.SECONDS.sleep(4);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        System.out.println("发消息");
    }

    public void hello(){
    
    
        System.out.println("Hello");
    }

}

答案:先执行线程B,再执行线程A

1、hello()方法上没有锁,线程B执行的时候就不存在排队等锁
2、线程A睡眠了4S,所以线程B就先跑完了


4、两个对象,两个同步方法,谁先执行?

public class Lock {
    
    

    public static void main(String[] args) {
    
    

        Phone phone1 = new Phone();

        Phone phone2 = new Phone();

        new Thread(() -> {
    
    
            phone1.message();
        }, "线程A").start();


        new Thread(() -> {
    
    
            phone2.call();
        }, "线程B").start();

    }

}


class Phone {
    
    

    /**
     * 发短信的线程 睡眠四秒
     */
    public synchronized void message() {
    
    
        try {
    
    
            TimeUnit.SECONDS.sleep(4);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        System.out.println("发消息");
    }

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

}

答案:先执行线程B,再执行线程A

1、phone1和phone2是两个对象,他们都是调用者,也就意味着他们两个都获取了各自的锁,互不干涉
2、所以不存在谁等谁,线程A睡眠4S,所以线程B先执行完毕
3、如果线程A没有延迟,就是随机的


5、增加两个静态同步方法,只有一个对象,谁先执行?

public class Lock {
    
    

    public static void main(String[] args) {
    
    

        Phone phone = new Phone();
        
        new Thread(() -> {
    
    
            phone.message();
        }, "线程A").start();


        new Thread(() -> {
    
    
            phone.call();
        }, "线程B").start();

    }

}

class Phone {
    
    

    /**
     * 发短信的线程 睡眠四秒
     */
    public static synchronized void message() {
    
    
        try {
    
    
            TimeUnit.SECONDS.sleep(4);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        System.out.println("发消息");
    }

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

}

答案:先执行线程A,再执行线程B

1、这个问题和第一个很相似,只多了一个static,但是这边的解答是不同的
2、因为class Phone{}里面的两个方法都是static修饰的,所以类一加载的时候就存在了,我们这边锁的是Phone这个class对象
3、如果这边不明白,接着看下一问


6、两个对象,两个静态同步方法,谁先执行?

public class Lock {
    
    

    public static void main(String[] args) {
    
    

        Phone phone1 = new Phone();

        Phone phone2 = new Phone();


        new Thread(() -> {
    
    
            phone1.message();
        }, "线程A").start();


        new Thread(() -> {
    
    
            phone2.call();
        }, "线程B").start();

    }

}

class Phone {
    
    

    /**
     * 发短信的线程 睡眠四秒
     */
    public static synchronized void message() {
    
    
        try {
    
    
            TimeUnit.SECONDS.sleep(4);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        System.out.println("发消息");
    }


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

}

答案:先执行线程A,在执行线程B

1、回看第四问,两个对象,应该是两把锁,线程A延迟,应该是线程B先走完,为什么实际结果是线程A先走完呢?
2、这边就是上一问回答的static修饰的类,一加载的时候就存在了,所以这边锁的是class对象


7、一个对象,一个静态同步方法,一个普通同步方法,谁先执行?

public class Lock {
    
    

    public static void main(String[] args) {
    
    

        Phone phone = new Phone();

        new Thread(() -> {
    
    
            phone.message();
        }, "线程A").start();


        new Thread(() -> {
    
    
            phone.call();
        }, "线程B").start();

    }

}

class Phone {
    
    

    /**
     * 发短信的线程 睡眠四秒
     * 静态同步方法
     */
    public static synchronized void message() {
    
    
        try {
    
    
            TimeUnit.SECONDS.sleep(4);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        System.out.println("发消息");
    }

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

}

答案:先执行线程B,再执行线程A

1、static message()锁的是类模板(这边能理解锁class对象了吧)
2、call()方法锁的是调用者
3、两个线程不是调用一把锁,而线程A睡眠了,所以线程B先执行完毕


8、两个对象,一个静态同步方法,一个普通同步方法,谁先执行?

public class Lock {
    
    

    public static void main(String[] args) {
    
    

        Phone phone1 = new Phone();

        Phone phone2 = new Phone();

        new Thread(() -> {
    
    
            phone1.message();
        }, "线程A").start();


        new Thread(() -> {
    
    
            phone2.call();
        }, "线程B").start();

    }

}

class Phone {
    
    

    /**
     * 发短信的线程 睡眠四秒
     * 静态同步方法
     */
    public static synchronized void message() {
    
    
        try {
    
    
            TimeUnit.SECONDS.sleep(4);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        System.out.println("发消息");
    }

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

}

答案:先执行线程B,再执行线程A

1、这边大家应该都能知道答案了,两个对象根本不是用的同一把锁,线程A睡眠4S,所以肯定是线程B先跑完了


参考文献:B站狂神说JAVA

猜你喜欢

转载自blog.csdn.net/weixin_45452416/article/details/110482506