8锁现象其实就是关于锁的8个问题。
8锁现象一直都是JavaJUC面试的高频考点。
简单来说,我们需要永远判断以下三个问题,即
如何判断锁的是谁!永远知道什么锁,锁到底锁的是谁!
下面通过一个简单的场景来彻底玩转8锁现象!
定义一个资源类People,里面有两个经过synchronized上锁后的方法,一个exercise(),方法里面执行输出跑步,一个study(),方法里面输出考研。

1.第一个问题

1.定义一个people对象,两个线程执行 先输出跑步还是考研?
具体代码如下所示:

package com.lock8;
import java.util.concurrent.TimeUnit;
/*
* 1.定义一个people对象,两个线程执行 先执行跑步还是考研?
* */
public class Test1 {public static void main(String[] args) {People people = new People();new Thread(()->{people.exercise();},"A").start();//这里睡眠1s 当然也可以不睡眠try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(()->{people.study();},"B").start();}
}
class People{public synchronized void exercise(){System.out.println("跑步");}public synchronized void study(){System.out.println("考研");}
}

结果展示:

原因分析:
原因不仅仅是应为线程A的people.exercise()先执行,究其根本是因为在People资源类的方法里面有synchronized的存在,而synchronized的对象是方法的调用者,我们这里只有一个People实例,即只有一个调用者,所以此时两个方法用的是同一个锁,即谁先拿到该锁便谁先执行!

2.第二个问题

2.还是两个线程执行 但是exercise方法内部首先延迟4秒 此种情况下先输出跑步还是考研?

package com.lock8;
import java.util.concurrent.TimeUnit;public class Test1 {public static void main(String[] args) {People people = new People();new Thread(()->{people.exercise();},"A").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(()->{people.study();},"B").start();}
}class People{public synchronized void exercise(){try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("跑步");}public synchronized void study(){System.out.println("考研");}
}

结果展示:

原因分析:
这里虽然在exercise方法内部延迟4s,但是我们在study和exercise方法上面加上了synchronized锁,且只定义了一个people对象,即两个线程的上锁的是同一个对象,即exercise在延迟的同时没有释放锁,所以study方法会在exercise方法执行之后再执行!

3.第三个问题

3.增加了一个普通方法exam(),在exam里面输出考试,同样是两个线程执行,一个线程执行exercise方法,一个线程执行非同步方法exam,是先输出跑步还是考试?

package com.lock8;
import java.util.concurrent.TimeUnit;public class Test2 {public static void main(String[] args) {People1 people = new People1();new Thread(()->{people.exercise();},"A").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(()->{people.exam();},"B").start();}
}class People1{public synchronized void exercise(){try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("跑步");}public synchronized void study(){System.out.println("考研");}public void exam(){System.out.println("考试");}
}

结果展示:

原因分析:
由于非同步方法exam上面没有上synchronized锁,不受锁的影响,所以在exercise内部延迟的同时,程序就会先输出考试,接着等延迟过后在输出跑步!

4.第四个问题

4.定义两个资源对象,两个线程执行两个同步方法,先输出考研还是跑步?

package com.lock8;
import java.util.concurrent.TimeUnit;
public class Test2 {public static void main(String[] args) {People1 people1= new People1();People1 people2= new People1();new Thread(()->{people1.exercise();},"A").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(()->{people2.study();},"B").start();}
}class People1{public synchronized void exercise(){try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("跑步");}public synchronized void study(){System.out.println("考研");}public void exam(){System.out.println("考试");}
}

结果展示:

原因分析:
由于synchronized锁的是方法的调用者,但是我们这里定义了两个对象,即每个对象都有一把锁,就有两把锁,所以当第一个对象的exercise方法被延迟后,程序会自动调用第二个对象的study方法,因此锁的不是同一个对象,因此会先输出考研后输出跑步。

5.第五个问题

5.增加两个静态的同步方法,即在exercise方法和study方法前面加一个static关键字,将方法都变为静态方法

package com.lock8;
import java.util.concurrent.TimeUnit;public class Test3 {public static void main(String[] args) {People2 people= new People2();new Thread(()->{people.exercise();},"A").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(()->{people.study();},"B").start();}
}class People2{public static synchronized void exercise(){try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("跑步");}public static synchronized void study(){System.out.println("考研");}public void exam(){System.out.println("考试");}
}

原因分析:
这里的方法用static关键字修饰,用static修饰的锁,锁的不是方法调用者本身,而锁的是类的模板,即Class模板,而众多的类实例只有一个类模板,因此锁的类模板也是全局唯一。所以只要exercise方法不释放锁,就不会执行study方法。

6.第六个问题

6.这里定义两个类对象,同样增加两个静态同步方法,先输出跑步还是考研?

package com.lock8;
import java.util.concurrent.TimeUnit;
public class Test3 {public static void main(String[] args) {People2 people1= new People2();People2 people2= new People2();new Thread(()->{people1.exercise();},"A").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(()->{people2.study();},"B").start();}
}class People2{public static synchronized void exercise(){try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("跑步");}public static synchronized void study(){System.out.println("考研");}public void exam(){System.out.println("考试");}
}

结果展示:

原因分析:
由于这次采用的static同步锁,因此锁的是类模板对象,而众多类实例的类模板全局唯一。(当类的.class文件经过类加载器后,就会将类模板初始化加载到JVM的方法区中,而众多的类实例都是通过类模板映射加载,可以输出众多实例的类模板的hashCode来证明!)同样的,由于上锁的是同一个对象,即Class类模板,因此主要exercise方法不释放锁,无论延迟多久,都不会执行study方法,因此会先输出跑步!

7.第七个问题

7.一个静态同步方法,一个普通的同步方法,只定义一个对象,先输出跑步还是考研?

package com.lock8;
import java.util.concurrent.TimeUnit;public class Test4 {public static void main(String[] args) {People3 people= new People3();new Thread(()->{people.exercise();},"A").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(()->{people.study();},"B").start();}
}class People3{//静态同步方法public static synchronized void exercise(){try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("跑步");}//同步方法public synchronized void study(){System.out.println("考研");}public void exam(){System.out.println("考试");}
}

结果展示:

原因分析:
此种情况,普通的同步方法(synchronized)锁的是方法的调用者,而静态同步方法(static synchronized )锁的是Class类模板,即两个方法锁的不是同一个对象,所以后面调用的方法不需要去等待前一个延迟额锁,及时exercise方法不释放锁,我也可以去执行study方法,因此会先输出考研!
简单来说:如果两个锁上锁的是同一个对象,则必须要等到一个方法执行结束才能执行下一个方法,而当两个锁上锁的不是同一个对象,则当一个方法发生执行延迟时,就可以先执行下一个方法!

8.第八个问题

8.一个静态同步方法,一个普通的同步方法,定义两个对象,先输出跑步还是考研?

package com.lock8;
import java.util.concurrent.TimeUnit;public class Test4 {public static void main(String[] args) {People3 people1= new People3();People3 people2= new People3();new Thread(()->{people1.exercise();},"A").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(()->{people2.study();},"B").start();}
}class People3{//静态同步方法public static synchronized void exercise(){try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("跑步");}//普通同步方法public synchronized void study(){System.out.println("考研");}public void exam(){System.out.println("考试");}
}

结果展示:

原因分析:
此时一个普通同步方法,锁的是方法的调用者一个静态同步方法,锁的是Class类模板,即表示二者上锁的不是同一个对象,所以不用等待exercise方法执行结束再去执行study方法,因此也会先输出考研!

小结

我们可以采用synchronized 给方法上锁,即表示方法是普通同步锁,使用synchronized 上锁的是方法的调用者;
我们也可以采用static synchronized 给方法上锁,表示方法是静态的同步方法锁,使用static synchronized 上锁锁的是资源类的Class类模板,而Class类模板是全局唯一;
规律:如果上锁的是同一个对象,则执行会按照先后顺序依次执行,只有前面的方法执行结束,才能执行后面的方法;如果上锁的不是同一个对象,首先也会按照先后顺序执行,一旦执行的过程中发现有sleep方法,即延迟,有CPU的空闲,就会执行下面与它不是同一个锁的方法,提高CPU执行的效率!

深入理解JUC的8锁现象相关推荐

  1. 狂神JUC——8锁现象彻底理解锁

    JUC 1 狂神JUC--传统的Synchronized锁 2 狂神JUC--Lock锁 3 狂神JUC--Lock版生产者和消费者 4 狂神JUC--Synchronized版的生产者和消费者问题 ...

  2. CMOS器件闩锁现象分析与讨论-Good

    CMOS器件的输入端电平如超出电源电压或地电平,将会闩锁 在目前的硬件电路设计中,CMOS器件大量使用,并且器件的速度越来越高,集成度越来越大,因此对器件的可靠性要求也不断提高.随着工艺和器件设计水平 ...

  3. 狂神JUC八种锁的理解

    1.标准情况下.两个线程先打印 打电话还是发短信? 2.sendSms延迟4秒,两个线程先打印打电话还是发短信? package com.company.lock_8;/*** 8锁问题,就是关于锁的 ...

  4. 面试必备系列JUC(6)--八锁详解

    文章目录 前言 一.锁总述 二.乐观锁 VS 悲观锁 2.1 基本概念 2.2 实现方式 2.3 优缺点和适应场景 三.公平锁 VS 非公平锁 3.1 基本概念 3.2 源码解析 四.可重入锁 VS ...

  5. juc java_深入理解JUC(java.util.concurrent)

    Concurrent下的核心类 Executor:具有runnable任务的执行者 ExecutorService:一个线程池管理者,实现类有多种,能把runnable,callable提交到线程池中 ...

  6. Java并发编程—JUC的Lock锁

    一.Lock (JUC锁) JUC 锁位于java.util.concurrent.locks包下,为锁和等待条件提供一个框架,它不同于内置同步和监视器. CountDownLatch,CyclicB ...

  7. 深入理解mysql系列_深入理解MySQL系列之锁

    按锁思想分类 悲观锁 优点:适合在写多读少的并发环境中使用,虽然无法维持非常高的性能,但是在乐观锁无法提更好的性能前提下,可以做到数据的安全性 缺点:加锁会增加系统开销,虽然能保证数据的安全,但数据处 ...

  8. 可控硅失效现象_闩锁现象及其防护

    我们根据电阻端接的介绍可以知道:我们通过在电路输出管脚出串联一个33Ω的电阻.但是我们我们可以观察到有些电路中的信号,竟然有串联220Ω,甚至1kΩ.这是为什么呢?首先我们一般在低速的接口看到这样的设 ...

  9. 深入理解Mysql - 事务与锁原理

    一.事务的定义 事务是数据库管理系统执行过程中的一个逻辑单位,有一个有限的数据库操作序列构成.mysql 的存储引擎中只有InnoDB支持事务. 二.事务的四大特性 原子性(Atomicity):原子 ...

最新文章

  1. java打印版本兼容_打印class文件的Java编译器内部的版本号
  2. 解决Ubuntu下 Could NOT find CURL (missing: CURL_LIBRARY CURL_INCLUDE_DIR)
  3. MyBatisPlus条件构造器实现降序排序的两种方式
  4. mysql 多数据源访问_通过Spring Boot配置动态数据源访问多个数据库的实现代码
  5. c 的dll php调用方法,PHP调用C#开发的dll类库方法,_PHP教程
  6. css禁止鼠标复制文本的属性:user-select
  7. JS面向对象之创建对象
  8. 使用docker安装设置oracle
  9. C#通过序列化实现深表复制
  10. 3年后准确率仍达97%:利用谷歌语音转文本 API 绕过reCAPTCHA
  11. ReleaseDC和DeleteDC的区别
  12. php清空session值_删除与销毁session
  13. SAP BTP 简介以及演化历史
  14. 直流双闭环pwm调速系统matlab仿真,基于Matlab的双闭环PWM直流调速虚拟实验系统
  15. Android Socket 编程(WIFI 和 ADB)
  16. cdr怎么做文字路径_CorelDRAW如何制作环绕圆形的路径文字
  17. 送客户的祝福语_送给客户的温馨祝福语
  18. 查看详细Windows VISTA 或Windows 7激活状态
  19. shared_ptr中的owner_before解析(最易懂,最全面)
  20. [渝粤教育] 中国地质大学(武汉) 走近国粹 中国陶瓷 参考 资料

热门文章

  1. Spring三级缓存解决循环依赖
  2. 00 后程序员就要为“你”加班?呵呵
  3. Marva Collin's Way
  4. 从数据中台到全链路数据生产力
  5. 王树森强化学习笔记——多智能体强化学习
  6. django文件上传功能的实现
  7. 几招搞定如何发送招聘兼职通知面试短信
  8. 智创万物,数赢未来——如何助推数智时代的发展浪潮
  9. @RequiredArgsConstructor 代替@AutoWired注解
  10. 大公司病了,这也太形象了吧!!!