Demo代码
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(() -> {
phone.sendEmail();
}, "AA").start();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone.sendSMS();
// phone.sayHello();
// phone1.sendSMS();
}, "BB").start();
}
}
class Phone {
static synchronized void sendEmail() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("------------SendEmail");
}
// static synchronized void sendSMS() {
// System.out.println("------------sendSMS");
// }
synchronized void sendSMS() {
System.out.println("------------sendSMS");
}
void sayHello() {
System.out.println("Hello");
}
}
问题:
- 邮件方法线程睡眠2秒
- 短信方法正常执行
- 标准访问,请问先打印还是邮件还是短信?
- 首先邮件,然后短信
- 在邮件方法内睡四秒,先打印短信还是邮件?
- 首先停2秒,先邮件,后短信
- 普通的hello方法,是先打邮件还是hello?
- 先执行普通方法,后执行邮件
- 现在有两部手机,先打印短信还是邮件?
- 先执行线程没有延迟的。
- 先短信,延迟2秒,后邮件
- 两个静态同步方法,1部手机,先打印短信还是邮件?
- 首先停2秒,先邮件,后短信
- 两个静态同步方法,2部手机,先打印短信还是邮件?
- 首先停2秒,先邮件,后短信
- 1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件?
- 先短信,停2秒,后邮件
- 1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件?
- 先短信,停2秒,后邮件
结论
-
对于普通方法:
- 同一时刻,只能有一个线程调用该类的一个同步方法,也就是说,不管当前类有多少个同步方法,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法
- 锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
- 虽然手机有多个功能,但是同一时间只能有一个人去操作当前手机,即使手机功能非常多,其他人也只能等待。
-
加个普通方法后发现和同步锁无关,二者之间没有任何联系,执行顺序不受影响。
-
换成两个对象之后,即使加锁,也不是同一把锁,所以相互之间不受影响,各自执行。
-
synchronized实现同步的基础:Java中的每一个对象都可以作为锁。
- 对于普通同步方法,锁是当前实例对象。(this)
- 对于静态同步方法,锁是当前类的Class对象。
- 对于同步方法块,锁是Synchonized括号里配置的对象
-
当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,可是别的实例对象的非静态同步方法因为跟当前实例对象的非静态同步方法用的是不同的锁,所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
-
所有的静态同步方法用的也是同一把锁——类对象本身(Class),这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。
-
但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!
-
思考:static 修饰的属性方法和普通属性方法加载时间。