• 并发编程【八锁问题】


    一、场景一

    两个同步方法,一部手机,请问先打印邮件还是短信?

    class Phone{
        public synchronized void sendEmail() throws Exception{
            System.out.println("*******sendEmail");
        }
        public synchronized void sendMs() throws Exception{
            System.out.println("*******sendMs");
        }
    }
    
    public class TestDemo {
        public static void main(String[] args) throws InterruptedException {
            Phone phone = new Phone();
    
            new Thread(()->{
                try {
                    phone.sendEmail();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },"A").start();
    
            Thread.sleep(100);
    
            new Thread(()->{
                try {
                    phone.sendMs();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },"B").start();
        }
    }
    

    结果:

    sendEmail

    sendMs

    分析:

    因为 Thread.sleep(100); 这一行代码保证了一定会让 A 线程先启动,所以线程 A 先获取到锁对象(this对象),而线程 B 无法获取到锁对象,所以线程 A 先执行,然后才是线程 B 执行。

    二、场景二

    发送邮件的方法睡眠4秒钟,请问先打印邮件还是短信?

    class Phone{
        public synchronized void sendEmail() throws Exception{
        	try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("*******sendEmail");
        }
        public synchronized void sendMs() throws Exception{
            System.out.println("*******sendMs");
        }
    }
    
    public class TestDemo {
        public static void main(String[] args) throws InterruptedException {
            Phone phone = new Phone();
    
            new Thread(()->{
                try {
                    phone.sendEmail();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },"A").start();
    
            Thread.sleep(100);
    
            new Thread(()->{
                try {
                    phone.sendMs();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },"B").start();
        }
    }
    

    结果:

    sendEmail

    sendMs

    分析:

    因为 Thread.sleep(100); 这一行代码保证了一定会让 A 线程先启动,所以线程 A 先获取到锁对象(this对象),而线程 B 无法获取到锁对象,所以线程 A 先执行,A 执行完毕后才是线程 B 执行。

    三、场景三

    新增一个普通方法 hello(),请问先打印邮件还是 hello?

    class Phone{
        public synchronized void sendEmail() throws Exception{
        	try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("*******sendEmail");
        }
        public synchronized void sendMs() throws Exception{
            System.out.println("*******sendMs");
        }
        public void sayHello() throws Exception{
            System.out.println("*****sayHello");
        }
    }
    
    public class TestDemo {
        public static void main(String[] args) throws InterruptedException {
            Phone phone = new Phone();
    
            new Thread(()->{
                try {
                    phone.sendEmail();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },"A").start();
    
            Thread.sleep(100);
    
            new Thread(()->{
                try {
                    phone.sayHello();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },"B").start();
        }
    }
    

    结果:

    sayHello

    endEmail

    分析:

    因为 Thread.sleep(100); 这一行代码保证了一定会让 A 线程先启动,所以线程 A 先获取到锁对象(this对象),而线程 B 调用的 sayHello 方法不需要 获取锁,所以在线程 A 睡眠的时候,线程 B 可以直接运行。

    四、场景四

    两部手机,请问先打印邮件还是短信?

    class Phone{
        public synchronized void sendEmail() throws Exception{
        	try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("*******sendEmail");
        }
        public synchronized void sendMs() throws Exception{
            System.out.println("*******sendMs");
        }
    }
    
    public class TestDemo {
        public static void main(String[] args) throws InterruptedException {
            Phone phone = new Phone();
    		Phone phone2 = new Phone();
            
            new Thread(()->{
                try {
                    phone.sendEmail();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },"A").start();
    
            Thread.sleep(100);
    
            new Thread(()->{
                try {
                    phone2.sendMs();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },"B").start();
        }
    }
    

    结果:

    sendMs

    sendEmail

    分析:

    因为有两个手机实例,所以存在两个 this 对象,因此两个线程获取的锁对象不是同一个,所以线程 A、B 不存在锁的争夺情况,从而在线程 A 睡眠期间,线程 B 可以运行。

    五、场景五

    两个静态同步方法,同一部手机,请问先打印邮件还是短信?

    class Phone{
        public static synchronized void sendEmail() throws Exception{
        	try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("*******sendEmail");
        }
        public static synchronized void sendMs() throws Exception{
            System.out.println("*******sendMs");
        }
    }
    
    public class TestDemo {
        public static void main(String[] args) throws InterruptedException {
            Phone phone = new Phone();
            
            new Thread(()->{
                try {
                    phone.sendEmail();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },"A").start();
    
            Thread.sleep(100);
    
            new Thread(()->{
                try {
                    phone.sendMs();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },"B").start();
        }
    }
    

    结果:

    sendEmail

    sendMs

    分析:

    因为 Thread.sleep(100); 这一行代码保证了一定会让 A 线程先启动,所以线程 A 先获取到锁对象(class对象),而线程 B 无法获取到锁对象,所以线程 A 先执行,A 执行完毕后才是线程 B 执行。

    六、场景六

    两个静态同步方法,两部手机,请问先打印邮件还是短信?

    class Phone{
        public static synchronized void sendEmail() throws Exception{
        	try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("*******sendEmail");
        }
        public static synchronized void sendMs() throws Exception{
            System.out.println("*******sendMs");
        }
    }
    
    public class TestDemo {
        public static void main(String[] args) throws InterruptedException {
            Phone phone = new Phone();
            Phone phone2 = new Phone();
            
            new Thread(()->{
                try {
                    phone.sendEmail();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },"A").start();
    
            Thread.sleep(100);
    
            new Thread(()->{
                try {
                    phone2.sendMs();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },"B").start();
        }
    }
    

    结果:

    sendEmail

    sendMs

    分析:

    因为 Thread.sleep(100); 这一行代码保证了一定会让 A 线程先启动,所以线程 A 先获取到锁对象(class对象),而线程 B 无法获取到锁对象,所以线程 A 先执行,A 执行完毕后才是线程 B 执行。

    补充:两部手机只能说明存在两个 this 对象,但是 class 对象还是只有一个。

    七、场景七

    一个普通同步方法,一个静态同步方法,一部手机,请问先打印邮件还是短信?

    class Phone{
        public static synchronized void sendEmail() throws Exception{
        	try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("*******sendEmail");
        }
        public synchronized void sendMs() throws Exception{
            System.out.println("*******sendMs");
        }
    }
    
    public class TestDemo {
        public static void main(String[] args) throws InterruptedException {
            Phone phone = new Phone();
            
            new Thread(()->{
                try {
                    phone.sendEmail();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },"A").start();
    
            Thread.sleep(100);
    
            new Thread(()->{
                try {
                    phone.sendMs();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },"B").start();
        }
    }
    

    结果:

    sendMs

    sendEmail

    分析:

    尽管 Thread.sleep(100); 这一行代码保证了一定会让 A 线程先启动,所以线程 A 先获取到锁对象(class对象);

    但是因为线程 B 是调用的同步方法,锁对象为 this 对象,所以两个线程的锁对象不是同一个,不存在争用的情况,所以在线程 A 睡眠的时候,线程 B 可以继续执行。

    八、场景八

    一个普通同步方法,一个静态同步方法,两部手机,请问先打印邮件还是短信?

    class Phone{
        public static synchronized void sendEmail() throws Exception{
        	try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("*******sendEmail");
        }
        public synchronized void sendMs() throws Exception{
            System.out.println("*******sendMs");
        }
    }
    
    public class TestDemo {
        public static void main(String[] args) throws InterruptedException {
            Phone phone = new Phone();
            Phone phone2 = new Phone();
            
            new Thread(()->{
                try {
                    phone.sendEmail();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },"A").start();
    
            Thread.sleep(100);
    
            new Thread(()->{
                try {
                    phone2.sendMs();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },"B").start();
        }
    }
    

    结果:

    sendMs

    sendEmail

    分析:

    尽管 Thread.sleep(100); 这一行代码保证了一定会让 A 线程先启动,所以线程 A 先获取到锁对象(class对象);

    但是因为线程 B 是调用的同步方法,锁对象为 this 对象,所以两个线程的锁对象不是同一个,不存在争用的情况,所以在线程 A 睡眠的时候,线程 B 可以继续执行。

    补充:两部手机只能说明存在两个 this 对象,但因为两个线程中一个用的 class,一个用的 this,所以还是两个不同的锁对象。

    九、总结

    其实这些都是关于锁对象的问题,只需要正确的分析出锁对象,即可解决问题。

    若有错误,欢迎指正!

  • 相关阅读:
    BZOJ 1597: [Usaco2008 Mar]土地购买
    BZOJ 1005: [HNOI2008]明明的烦恼
    BZOJ 1004: [HNOI2008]Cards
    Burnside引理和Polya定理
    BZOJ 1003: [ZJOI2006]物流运输
    BZOJ 1002: [FJOI2007]轮状病毒
    BZOJ 1001: [BeiJing2006]狼抓兔子
    网络流 最大流dinic算法解释
    51nod 1299 监狱逃离
    2017.11.26【清华集训2017】模拟
  • 原文地址:https://www.cnblogs.com/Java-biao/p/14492884.html
Copyright © 2020-2023  润新知