synchronized锁原理详解

Dear 丶 2022-10-23 02:57 187阅读 0赞

synchronized锁用法

  1. // synchronized锁底层原理
  2. public class SynchronizedTest03 {
  3. //修饰静态方法(同步方法)
  4. //代码块0(锁定的是当前类)
  5. public synchronized static void access0(){
  6. try {
  7. TimeUnit.MINUTES.sleep(1);
  8. System.out.println(Thread.currentThread().getName()+" is running");
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. }
  13. //修饰非静态方法(同步方法)
  14. //代码块1(锁定的是当前对象的调用者)
  15. public synchronized void access1(){
  16. try {
  17. TimeUnit.MINUTES.sleep(1);
  18. System.out.println(Thread.currentThread().getName()+" is running");
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. //修饰非静态方法(同步代码块)
  24. //代码块2(锁定的是对象)this指的是当前对象
  25. public void access2(){
  26. synchronized(this){
  27. try {
  28. synchronized (this){
  29. TimeUnit.MINUTES.sleep(1);
  30. }
  31. System.out.println(Thread.currentThread().getName()+" is running");
  32. } catch (InterruptedException e) {
  33. e.printStackTrace();
  34. }
  35. }
  36. }
  37. //代码块3(锁定的是CLASS类)(同步代码块)
  38. public void access3(){
  39. synchronized(SynchronizedTest03.class){//ClassLoader class --> “堆”区生成一个Class对象:所有的对象
  40. //有Class对象的所有的对象都共同使用这一个锁
  41. try {
  42. TimeUnit.MINUTES.sleep(1);
  43. System.out.println(Thread.currentThread().getName()+" is running");
  44. } catch (InterruptedException e) {
  45. e.printStackTrace();
  46. }
  47. }
  48. }
  49. public static void main(String[] args) {
  50. SynchronizedTest03 demo = new SynchronizedTest03();
  51. for (int i = 0; i < 5; i++) {
  52. new Thread(demo::access2).start();
  53. }
  54. }
  55. }

锁的分类:

  1. 1.对象锁
  2. synchronized(this|object) \{\} 修饰非静态方法
  3. Java 中,**每个对象都会有一个** **monitor** **对象**,这个对象其实就**是 Java 对象的锁**,通常会被**称为“内置锁”或“对象锁”**。类的对象可以有多个,所以每个对象有其独立的对象锁,互不干扰。
  4. **monitor对象(监视器)**:
  5. A、某一线程占有这个对象的时候,先看monitor 的计数器是不是0,如果是0还没有线程占有,这个时候线程占有这个对象,并且对这个对象的monitor+1;如果不为0,表示这个线程已经被其他线程占有,这个线程等待。当线程释放占有权的时候,monitor-1
  6. B、同一线程可以对同一对象进行多次加锁,加一次锁就monitor+1,可重入性。
  7. 2.类锁
  8. synchronized(类.class) \{\} 修饰静态方法
  9. Java 中,针对每个类也有一个锁,可以称为“**类锁**”,类锁实际上是通过对象锁实现的,即**类的 Class 对象锁**。每个类只有一个 Class 对象,所以每个类只有一个类锁。

synchronized原理分析

1、线程堆栈分析(Jconsole、Jstack pid)

上面的程序我们启动5个线程,每个线程睡眠1分钟,我们使用Jconsole软件进行查看线程。

20210318105352114.png20210318105356132.png

我们可以看到有5个线程,一个线程正在等待,其他4个线程都是阻塞状态。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2OTQyNzIw_size_16_color_FFFFFF_t_70watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2OTQyNzIw_size_16_color_FFFFFF_t_70 1

使用Jstack pid查看线程:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2OTQyNzIw_size_16_color_FFFFFF_t_70 2

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2OTQyNzIw_size_16_color_FFFFFF_t_70 3

2、JVM指令分析

对当前类进行反编译:javap -v SynchronizedTest03

找到我们写的access0、access1、access2、access3方法:其中access0、access1原理相同(锁的方法),access2、access3原理相同(锁的代码块)

  1. A、锁的代码块(用monitor进行加锁解锁)

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2OTQyNzIw_size_16_color_FFFFFF_t_70 4

【问题】为什么会有两个monitorexit?

  1. 答:第一个是正常出口,第二个是异常出口。
  2. B、锁的方法(用标记进行加锁、ACC\_SYNCHRONIZED

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2OTQyNzIw_size_16_color_FFFFFF_t_70 5

3、使用synchronized注意的问题

(1)与moniter关联的对象不能为空

(2)synchronized作用域太大

(3)不同的monitor企图锁相同的方法

(4)多个锁的交叉导致死锁

Java虚拟机对synchronized的优化

对锁优化加入:偏向锁、轻量级锁、重量级锁(等待时间长)

每一个monitor存放在一个实例对象里面,每一个对象都和monitor进行关联,主要与对象头进行关联,关联主要根据锁的状态进行。

20210318105712282.png20210318105716781.png

无锁状态:没有加锁

偏向锁:在对象第一次被某一线程占有的时候,是否偏向锁置1,锁表01,写入线程号,当其他的线 程访问的时候,会去竞争,竞争失败(升级到轻量级锁)、很多次被第一次占有它的线程获取次数多—竞争成功

轻量级锁:线程有交替适用,互斥性不是很强,CAS失败,00

重量级锁:强互斥,10,等待时间长

自旋锁:竞争失败的时候,不是马上转化级别,而是执行几次空循环

锁消除:JIT在编译的时候吧不必要的锁去掉

发表评论

表情:
评论列表 (有 0 条评论,187人围观)

还没有评论,来说两句吧...

相关阅读

    相关 20.synchronized原理详解

    在java中,最简单的同步方式就是利用 synchronized 关键字来修饰代码块或者修饰一个方法,那么这部分被保护的代码,在同一时刻就最多只有一个线程可以运行,而 sync