前言
synchronize的8锁现象,其实就是利用锁的8种应用场景的代码,带你彻底搞懂:
synchronized 锁的对象是方法的调用者
下面我们开始看例子
例子1
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
//这里注意不是先调用 phone.sendSms(),而是因为锁的存在
new Thread(()->{
phone.sendSms();
},"A").start();
// 捕获
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {e.printStackTrace();
}
new Thread(()->{
phone.call();
},"B").start();
}
}
class Phone{
// synchronized 锁的对象是方法的调用者!、
// 两个方法用的是同一个锁,谁先拿到谁执行!
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
结果:等4秒钟会先出现发短信,再出现打电话
那么为什么会是这种结果,并不是因为A先执行,原因:
- synchronized 锁的对象是方法的调用者!
- 两个方法用的是同一个锁,谁先拿到谁执行!
例子2
public class Test2 {
public static void main(String[] args) {
// 两个对象,两个调用者,两把锁!
Phone2 phone1 = new Phone2();
Phone2 phone2 = new Phone2();
//锁的存在
new Thread(()->{
phone1.sendSms();
},"A").start();
// 捕获
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}}
class Phone2{
// synchronized 锁的对象是方法的调用者!
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
// 这里没有锁!不是同步方法,不受锁的影响
public void hello(){
System.out.println("hello");
}
}
运行结果:1秒钟后打电话,等待4秒发短信。
其实只要记住synchronized
锁的对象是方法的调用者!
Phone2 phone1 = new Phone2();
Phone2 phone2 = new Phone2();
是2个对象,不是去抢一个对象的锁。所以是先执行打电话。要区分锁的是谁。
例子3
这次的例子和之前的不太一样,再来2组测试,例子3和例子4
public class Test3 {
public static void main(String[] args) {
Phone phone1 = new Phone();
Phone phone2 = new Phone();
//锁的存在
new Thread(()->{
phone1.sendSms();
},"A").start();
// 捕获
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
class Phone{
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
}
运行结果:等待4秒先执行发短信,在执行打电话
大家可能会疑惑为什么和上边的例子不一样,明明也是2个对象。
静态方法和普通方法区别?静态方法是类一加载就有了!
那是因为Phone
这个对象里的synchronized
方法被static修饰,是类调用,锁的是Class,一个类只有1个class对象。
所以注意少写这种代码
例子4
和上边的例子3大家做对比
public class Test4 {
public static void main(String[] args) {
Phone phone1 = new Phone();
Phone phone2 = new Phone();
//锁的存在
new Thread(()->{
phone1.sendSms();
},"A").start();
// 捕获
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
// Phone唯一的一个 Class 对象
class Phone{
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
// 普通的同步方法 锁的调用者
public synchronized void call(){
System.out.println("打电话");
}
}
发现Phone类里的call
方法是普通方法,非静态方法,所以调用时不属于争抢同一把锁。
结果也就是先打电话,4秒后发短信。