阿里巴巴规约

【强制】高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。

说明:尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用 RPC 方法。

八种情况演示锁运行案例

问题

给你如下两个线程,请问先打印邮件还是短信?

1.标准访问有 A B 两个线程

Lock8Demo.java
class Phone {
    public synchronized void sendEmail() {
        System.out.println("sendEmail");
    }
 
    public synchronized void sendSMS() {
        System.out.println("sendSME");
    }
}
 
public class Lock8Demo {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> {
            phone.sendEmail();
        }, "A").start();
 
        try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
 
        new Thread(() -> {
            phone.sendSMS();
        }, "B").start();
    }
}
Terminal
sendEmail
sendSME

2.sendEmail 方法中加入暂停 3 秒钟

Lock8Demo.java
class Phone {
    public synchronized void sendEmail() {
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("sendEmail");
    }
 
    public synchronized void sendSMS() {
        System.out.println("sendSME");
    }
}
 
public class Lock8Demo {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> {
            phone.sendEmail();
        }, "A").start();
 
        try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
 
        new Thread(() -> {
            phone.sendSMS();
        }, "B").start();
    }
}
Terminal
sendEmail
sendSME

3.添加一个普通的 hello 方法

Lock8Demo.java
class Phone {
    public synchronized void sendEmail() {
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("sendEmail");
    }
 
    public void hello() {
        System.out.println("hello");
    }
}
 
public class Lock8Demo {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> {
            phone.sendEmail();
        }, "A").start();
 
        try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
 
        new Thread(() -> {
            phone.hello();
        }, "B").start();
    }
}
Terminal
hello
sendSME

4.有两部手机

Lock8Demo.java
class Phone {
    public synchronized void sendEmail() {
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("sendEmail");
    }
 
    public synchronized void sendSMS() {
        System.out.println("sendSME");
    }
}
 
public class Lock8Demo {
    public static void main(String[] args) {
        Phone phone = new Phone();
        Phone phone2 = new Phone();
        new Thread(() -> {
            phone.sendEmail();
        }, "A").start();
 
        try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
 
        new Thread(() -> {
            phone2.sendSMS();
        }, "B").start();
    }
}
Terminal
sendSME
sendEmail

5.有两个静态同步方法,有1部手机

Lock8Demo.java
class Phone {
    public static synchronized void sendEmail() {
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("sendEmail");
    }
 
    public static synchronized void sendSMS() {
        System.out.println("sendSME");
    }
}
 
public class Lock8Demo {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> {
            phone.sendEmail();
        }, "A").start();
 
        try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
 
        new Thread(() -> {
            phone.sendSMS();
        }, "B").start();
    }
}
Terminal
sendEmail
sendSME

6.有两个静态同步方法,有2部手机

Lock8Demo.java
class Phone {
    public static synchronized void sendEmail() {
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("sendEmail");
    }
 
    public static synchronized void sendSMS() {
        System.out.println("sendSME");
    }
}
 
public class Lock8Demo {
    public static void main(String[] args) {
        Phone phone = new Phone();
        Phone phone2 = new Phone();
        new Thread(() -> {
            phone.sendEmail();
        }, "A").start();
 
        try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
 
        new Thread(() -> {
            phone2.sendSMS();
        }, "B").start();
    }
}
Terminal
sendEmail
sendSMS

7.有1个静态同步方法,有1个普通同步方法,有1部手机

Lock8Demo.java
class Phone {
    public static synchronized void sendEmail() {
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("sendEmail");
    }
 
    public synchronized void sendSMS() {
        System.out.println("sendSME");
    }
}
 
public class Lock8Demo {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> {
            phone.sendEmail();
        }, "A").start();
 
        try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
 
        new Thread(() -> {
            phone.sendSMS();
        }, "B").start();
    }
}
Terminal
sendSME
sendEmail

8.有1个静态同步方法,有1个普通同步方法,有2部手机

Lock8Demo.java
class Phone {
    public static synchronized void sendEmail() {
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("sendEmail");
    }
 
    public synchronized void sendSMS() {
        System.out.println("sendSME");
    }
}
 
public class Lock8Demo {
    public static void main(String[] args) {
        Phone phone = new Phone();
        Phone phone2 = new Phone();
        new Thread(() -> {
            phone.sendEmail();
        }, "A").start();
 
        try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
 
        new Thread(() -> {
            phone2.sendSMS();
        }, "B").start();
    }
}

总结

案例1-2

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

案例3-4

  • 加个普通方法后发现和同步锁无关
  • 换成两个对象后,不是同一把锁了,情况立刻变化。

案例5-6

  • 换成静态同步方法后三种 synchronized 锁的内容有一些差别:
    • 对于普通同步方法,锁的是当前实例对象,通常指 this,具体的一部部手机,所有的普通同步方法用的都是同一把锁 ——> 实例对象本身;
    • 对于静态同步方法,锁的是当前类的 Class 对象,如 Phone.class 唯一的一个模板;
    • 对于同步方法块,锁的是 synchronized 括号内的对象。

案例7-8

  • 当一个线程试图访问同步代码时它首先必须得到锁,正常退出或抛出异常时必须释放锁;

  • 所有的普通同步方法用的都是同一把锁——实例对象本身,就是 new 出来的具体实例对象本身,本类 this

  • 也就是说如果一个实例对象的普通同步方法获取锁后,该实例对象的其他普通同步方法必须等待获取锁的方法释放锁后才能获取锁;

  • 所有的静态同步方法用的也是同一把锁——类对象本身,就是我们说过的唯一模板 Class

  • 具体实例对象 this 和唯一模板 Class,这两把锁是两个不同的对象,所以静态同步方法与普通同步方法之间是不会有竞态条件的;

  • 但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁。