以手机发邮件、发短信为例
class Phone {
public static synchronized void sendEmail() {
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("发邮件");
}
public synchronized void sendSms() {
System.out.println("发短信");
}
public void hello() {
System.out.println("Hello");
}
}
public class Lock8Demo {
public static void main(String[] args) throws Exception {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
phone.sendEmail();
}, "a").start();
// 为了保证a线程先执行
try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(() -> {
phone2.sendSms();
}, "b").start();
}
}
问题:
- 标准访问有ab两个线程,先打印邮件还是短信 (发邮件)
- sendEmail方法中加入暂停3秒钟,先打印邮件还是短信 ( 发邮件)
- 添加一个普通的hello方法,先打印邮件还是hello(hello)
- 有两部手机,先打印邮件还是短信 (发短信)
- 有两个静态同步方法,有一部手机,先打印邮件还是短信 (发邮件)
- 有两个静态同步方法,有两部手机,先打印邮件还是短信 (发邮件)
- 有一个静态同步方法,有一个普通同步方法,有一部手机,先打印邮件还是短信 (发短信)
- 有一个静态同步方法,有一个普通同步方法,有两部手机,先打印邮件还是短信 (发短信)
解析:
1-2:
一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,
其它的线程都只能等待。换句话说,某一个时刻内,只能有唯一的一个线程去访问这些synchronized方法
锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
3-4:
加个普通方法后发现和同步锁无关
换成两个对象后,不是同一把锁了,情况立刻变化
5-6:
都换成静态同步方法后,情况又变化
三种synchronized锁的内容有一些差别:
对于普通同步方法,锁的是当前实例对象,通常指this,具体的一部部手机,所有的普通同步方法用的都是同一把锁–>实例对象本身,
对于静态同步方法,锁的是当前类的Class对象,如Phone.class唯一的一个模板
对于同步方法块,锁的是synchronized括号内的对象
7-8:
当一个线程试图访问同步代码时它首先必须得到锁,正常退出或抛出异常时必须释放锁。
所有的普通同步方法用的都是同一把锁–实例对象本身,就是new出来的具体实例对象本身,本类this
也就是说如果一个实例对象的普通同步方法获取锁后,该实例对象的其他普通同步方法必须等待获取锁的方法释放锁后才能获得锁。
所有的静态同步方法用的也是同一把锁–类对象本身,就是我们说过的唯一模板CLass
具体实例对象this和唯一模板Class,这两把锁是两个不同的对象,所以静态同步方法与普通同步方法之间是不会有竞争条件的
但是一旦一个静态同步方法获取锁后,其它的静态同步方法都必须等待该方法释放锁后才能获取锁
我的总结:要明白锁住的是什么,锁住之后会阻塞哪些方法。
笔记摘自尚硅谷周阳老师。