synchronized对比cas

参考文档

CAS底层解析:https://www.cnblogs.com/Leo_wl/p/6899716.html

https://blog.csdn.net/tiandao321/article/details/80811103

各种锁类型的介绍:https://www.jianshu.com/p/e7aa0a5083fb

synchronized对比cas性能比较:https://www.jianshu.com/p/736c532869a3

synchronized对比cas使用介绍: https://www.cnblogs.com/konck/p/9393135.html

锁分类

乐观锁

读的时候不加锁,之后在写的时候才加锁。并且在写的时候,会比较当前值跟预期值是否一致,只有一致才会去执行写操作。乐观锁基本上都是由CAS来实现的。

悲观锁

读写的时候都加锁

公平锁

锁的获取遵循先进先出的原则

不公平锁

锁的获取遵循先进先出的原则

JAVA中的锁分类

偏向锁

当只有一个线程使用某个对象的情况下,该对象可以记录使用它的线程信息,如果一直都是同一个线程使用该对象,那么整个过程就不会加锁。

轻量级锁

当有两个或两个以上线程访问同一个对象时,偏向锁就不行了,此时就需要使用轻量级锁。其实就是乐观锁。

重量级锁

如果并发访问的线程很多,并且每个线程都要锁很长时间。此时轻量级锁就会不断的自旋检查,造成CPU被占满。此时就应该使用重量级锁,重量级锁会将等待锁的线程转入阻塞状态。虽然需要用户态和内核态的切换,但是避免了死循环自旋,大大降低了cpu的使用。

CAS compareAndSet

它是使用硬件特性来保证原子性。在底层调用unsafe.compareAndSwapObject方法实现原子操作,当然该方法内部也是一个硬件加锁的操作。流程就是该方法会指定要修改的对象、对象的现有值和修改值。如果对象实际值跟指定现有值一致,就用修改值替换现有值。如果正在被其他线程加锁就返回修改失败。然后会有自旋检查,循环执行该原子操作,直到获取锁并执行成功。如果不限制执行次数,可能会造成死循环。

优点

1、不用阻塞线程,并让线程在用户态和内核态直接切换,省去切换的时间,一次阻塞和唤醒操作就需要两次切换。

缺点

1、如果锁一直被其他线程占用,并且自旋操作没有设置最大次数,就会造成死循环,造成cpu占用过高。

2、只能保证某一个对象的原子性,并不能保证几个对象或一个线程同步操作。

实现应用

 1、java.util.concurrent下的很多类,比如Atomic开头的原子操作变量,ReentrantLock,都是根据CAS实现的。如下图示

synchronized

实现

在Java1.6之前,synchronized就是用悲观锁实现的。1.6之后根据不同的情况会在偏向锁、轻量级锁和重量级锁之间切换。并且是按照从轻到重的顺序切换,反之则不行。

使用方式

  1. 修饰实例方法,为当前实例加锁,进入同步方法前要获得当前实例的锁。
  2. 修饰静态方法,为当前类对象加锁,进入同步方法前要获得当前类对象的锁。
  3. 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码块前要获得给定对象的锁。

等待唤醒机制

synchronized的等待唤醒是通过notify/notifyAll和wait三个方法来实现的,这三个方法的执行都必须在同步代码块或同步方法中进行,否则将会报错。

wait方法的作用是使当前执行代码的线程进行等待,notify/notifyAll相同,都是通知等待的代码继续执行,notify只通知任一个正在等待的线程,notifyAll通知所有正在等待的线程。wait方法跟sleep不一样,他会释放当前同步代码块的锁,notify在通知任一等待的线程时不会释放锁,只有在当前同步代码块执行完成之后才会释放锁。

锁定字符串

https://www.cnblogs.com/xrq730/p/6662232.html

一般情况下,即使锁定字符串内容相同的不同字符串对象,是无法保证原子性操作的。但是如果调用字符串的方法intern方法,则会返回一个常量区的对象,并且在常量区,字符串值相同只有一个对象,就可以实现对同一个字符串对象进行加锁了。

原文地址:https://www.cnblogs.com/suntp/p/12290883.html