zoukankan      html  css  js  c++  java
  • 并发编程【八锁问题】

    一、场景一

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

    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,所以还是两个不同的锁对象。

    九、总结

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

    若有错误,欢迎指正!

  • 相关阅读:
    利用ApplicationListener和ContextRefreshedEvent加载自己的beanPool
    java对象转变为map
    Java通过poi创建Excel文件并分页追加数据
    Java通过poi读取excel中文件
    SpringMvc通过controller上传文件代码示例
    SpringCloud组件学习-图
    Java-线程间通信小结
    Java-关于Thread
    Java-对象及变量的并发访问小结
    java爬取免费HTTP代理 code-for-fun
  • 原文地址:https://www.cnblogs.com/Java-biao/p/14492884.html
Copyright © 2011-2022 走看看