1. 两个普通同步方法,先打印邮件还是短信

public class Phone {
    public synchronized void sendEmail() {
        System.out.println("-------sendEmail");
    }
    public synchronized void sendSMS() {
        System.out.println("-------sendSMS");
    }

    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> phone.sendEmail(), "a").start();
        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> phone.sendSMS(), "b").start();
    }
}

先打印邮件,因为一个对象里面如果有多个 synchronized 方法,某一个时刻内,只要一个线程去调用其中的一个 synchronized 方法,其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一的一个线程去访问这些 synchronized 方法,锁的是当前对象 this,被锁定后,其它的线程都不能进入到当前对象的其它的 synchronized 方法

2. sendEmail 方法暂停 3 秒钟,先打印邮件还是短信

public class Phone {
    public synchronized void sendEmail() {
        try {
            TimeUnit.MILLISECONDS.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-------sendEmail");
    }
    public synchronized void sendSMS() {
        System.out.println("-------sendSMS");
    }

    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> phone.sendEmail(), "a").start();
        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> phone.sendSMS(), "b").start();
    }
}

先打印邮件,同上,而且 sleep 不会释放锁

3. 新增一个普通的方法 hello,先打印邮件还是短信

public class Phone {
    public synchronized void sendEmail() {
        try {
            TimeUnit.MILLISECONDS.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-------sendEmail");
    }
    public void hello() {
        System.out.println("-------sendSMS");
    }

    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> phone.sendEmail(), "a").start();
        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> phone.hello(), "b").start();
    }
}

先打印短信,调用普通方法与同步锁无关

4. 两个对象,先打印邮件还是信息

public class Phone {
    public synchronized void sendEmail() {
        try {
            TimeUnit.MILLISECONDS.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-------sendEmail");
    }
    public synchronized void sendSMS() {
        System.out.println("-------sendSMS");
    }

    public static void main(String[] args) {
        Phone phone = new Phone();
        Phone phone2 = new Phone();
        new Thread(() -> phone.sendEmail(), "a").start();
        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> phone2.sendSMS(), "b").start();
    }
}

先打印短信,因为是两个对象,使用的不是同一把锁

5. 同一个对象,两个静态同步方法,先打印邮件还是短信

public class Phone {
    public static synchronized void sendEmail() {
        try {
            TimeUnit.MILLISECONDS.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-------sendEmail");
    }
    public static synchronized void sendSMS() {
        System.out.println("-------sendSMS");
    }

    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> phone.sendEmail(), "a").start();
        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> phone.sendSMS(), "b").start();
    }
}

先打印邮件,因为是类锁,锁的是当前类的 Class 对象

6. 不同的对象,两个静态同步方法,先打印邮件还是短信

public class Phone {
    public static synchronized void sendEmail() {
        try {
            TimeUnit.MILLISECONDS.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-------sendEmail");
    }
    public static synchronized void sendSMS() {
        System.out.println("-------sendSMS");
    }

    public static void main(String[] args) {
        Phone phone = new Phone();
        Phone phone2 = new Phone();
        new Thread(() -> phone.sendEmail(), "a").start();
        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> phone2.sendSMS(), "b").start();
    }
}

先打印邮件,同上

7. 一个静态同步方法,一个普通同步方法,先打印邮件还是短信

public class Phone {
    public static synchronized void sendEmail() {
        try {
            TimeUnit.MILLISECONDS.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-------sendEmail");
    }
    public synchronized void sendSMS() {
        System.out.println("-------sendSMS");
    }

    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> phone.sendEmail(), "a").start();
        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> phone.sendSMS(), "b").start();
    }
}

先打印短信,因为这两把锁是不同的对象,普通同步方法锁的是实例对象 this,静态同步方法锁的是唯一模板 Class,所以静态同步方法与普通同步方法之间是不会有竞态条件的

8. 两个对象,一个静态同步方法,一个普通同步方法,先打印邮件还是短信

public class Phone {
    public static synchronized void sendEmail() {
        try {
            TimeUnit.MILLISECONDS.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-------sendEmail");
    }
    public synchronized void sendSMS() {
        System.out.println("-------sendSMS");
    }

    public static void main(String[] args) {
        Phone phone = new Phone();
        Phone phone2 = new Phone();
        new Thread(() -> phone.sendEmail(), "a").start();
        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> phone2.sendSMS(), "b").start();
    }
}

先打印短信,同上

Q.E.D.


盛年不重来,一日难再晨。