多线程基石——synchronized锁

版权声明:本文为博主原创文章,欢迎转载哦 https://blog.csdn.net/wgp15732622312/article/details/81160566

1.2.1 synchronized锁

synchronized锁具有先天性的重入性。每个对象拥有一个计数器,当线程获取该对象锁后,计数器就会加1。释放锁后就会计数器减一。

任一对象都有自己的监视器,当这个对象由同步块或者这个对象的同步方法调用时,执行方法的线程必须先获取该对象的监视器才能进入同步块和同步方法。如果没有获取到监视器的线程将会被阻塞在同步方法的入口处。进入BLOKCKED状态。

线程获取syncronized的过程,线程首先尝试获取Object的监视器,如果获取失败的话,就会进入同步队列,状态变为BLOCKED状态。当监视器占有者释放后,在同步队列中的线程就会有机会重新获取监视器。

1.2.2 synchronized内存语义

释放锁的时候把值刷新到主内存中,然后线程获取锁的时候,会从主内存这个哦哦呢读取最新的值,然后进行执行代码逻辑。对于synchronized的特征,同一时刻只有一个线程能够获取对象的监视器,从而进度到同步代码块或者同步方法时,表现为互斥性。

 

1.2.3 CAS操作

CAS(v,o,n),包含三个值:V 内存地址的实际值,O 预期的值(旧值);N 更新的值。当V和O相同时,也就是说旧值和内存中实际的值相同包名值未被其他线程修改过,就可以把新值N赋给V。如果V和O不相同时,表明该值已经被其他线程改动。就会直接返回V的值。当多个线程使用CAS操作一个变量时,只有一个线程能成功,其余失败,失败的线程会重新尝试,当然也可以挂起线程。

 

1.2.4 对比synchronized和CAS

在存在线程竞争的情况下会出现线程阻塞和唤醒锁带来的性能问题。因为这是一种互斥同步。而CAS并不是武断的将线程挂起,当CAS操作失败后进行尝试。所以可以说是非阻塞同步的。

在CAS的场景应用中,有CAS实现类。Concurrency包的实现,Lock的实现,atomic包的实现类等都用到了CAS。

CAS存在的问题,ABA问题,自旋时间过长问题。只能保证一个共享变量的原子性操作。

1.3 锁优化

对于多线程竟态条件的出现并不是很常见,于是jdk1.6引入了引入了偏向锁,轻量级锁。对于新的这两把锁用到了Java对象头,对象头里包含了HashCode值,分代年龄和锁标记位。在jdk.16中锁状态一共有4种:无锁,偏向锁,轻量级,重量级。这些锁可以升级但不能降级。

扫描二维码关注公众号,回复: 4990120 查看本文章

1.3.1 偏向锁

线程访问同步块获取锁时,在对象头和栈帧中的锁记录存储锁偏向的线程ID,该线程在进入和退出同步块是不需要进行CAS操作来加锁和解锁的。只需要检测对象头的Mark Word里是否存储指向当前线程的偏向锁。测试成功,表示线程已经获得了锁。如果失败,就会再次进行测试,测试Mark Word 的偏向锁标识是否设置成1(表示当前为偏向锁),如果没有设置,则使用CAS竞争锁,如果设置了则使用CAS将对象头的偏向锁偏向当前线程。

1.3.2 偏向锁解锁

等到竞争才释放锁。当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。偏向锁撤销,需要等待全局安全点,它会首先暂停拥有偏向锁的线程,然后检查持有偏向锁的线程是否活着,未活,则把对象头置为无锁状态。否则,有用偏向锁的栈会执行。遍历偏向锁对象的锁记录,要么锁记录和Mark Word要么重新偏向其他线程,要么恢复到无锁状态或者标记对象不适合作为偏向锁。最后唤醒暂停的线程。

 

                                                                                                                                                                                                                                                                                                                                                         

  1. 线程1访问同步块
  2. 判断Mark Word偏向锁是否指向线程1。指向成功获得锁。
  3. 如果未指向,则检测是否为偏向锁状态。
  4. 如果为偏向锁,检测拥有该锁的线程2是否为活跃状态。
  5. 如果线程2,不活跃,则偏向锁置为无锁状态。
  6. 如果活跃,则升级为轻量级锁。线程1开始自旋获取锁。

1.3.3 轻量级锁

在当前线程的栈帧中创建锁记录空间,将对象头Mark Workld复制到锁记录。当线程尝试使用CAS将对象头的Mark World替换为指向锁记录的指针。如果成功,线程获得锁成功,如果失败,线程自旋获取锁。

解锁:使用CAS操作把对象头的Mark Word替换到对象头,成功,则没有竞争,失败,

则表示存在竞争,就膨胀为重量级锁。 

    详细记录

  1. 线程1栈帧创建存储锁记录的空间,对象头的Mark Word复制到锁记录中,
  2. CAS操作,尝试将对象头的Mark Word替换为指向该线程的指针。
  3. 如果失败,自旋锁来获取锁。

解锁过程

使用原子性的CAS操作,将Displaced Mark Word 替换到对象头。

如果失败,表示锁存在竞争,锁膨胀为重量级锁。

猜你喜欢

转载自blog.csdn.net/wgp15732622312/article/details/81160566