Java中的八种锁

来源:互联网 发布:培训java三个月靠谱吗 编辑:程序博客网 时间:2024/06/03 21:54

Java中的锁

  • java中锁的分类
    • 公平锁/非公平锁
    • 可重入锁
    • 独享锁/共享锁
    • 互斥锁/读写锁
    • 乐观锁/悲观锁
    • 分段锁
    • 偏向锁/轻量级锁/重量级锁
    • 自旋锁
  • 上面这些分类并不全是指所得状态 , 有的指锁的特性 , 有的指锁的设计。

一、公平锁/非公平锁

  1. 公平锁是指多个线程按照申请所得顺序来获取锁。
  2. 非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序, 有可能后申请锁的线程 , 先获取锁 。 有可能可能会造成优先级反转或者饥饿现象
  3. 对于java中ReentrantLock而言 , 通过构造函数指定该锁是否是公平锁 , 默认是非公平锁 。非公平锁的优点在于吞吐量比公平锁要大 。
  4. 对于Synchronized而言 , 也是一种非公平锁 。由于不像ReentrantLock是通过AQS来实现线程调度 , 所以并没有 任何办法使其变成公平锁 。

二、可重入锁

  1. 可重入锁又称为递归锁 , 是指在同一线程的外层获取锁的时候 , 在进入内层方法会自动获取锁 。
  2. 对于Java 的ReentrantLock而言 , 他的名字就可以看出是一个可重入锁 , 其名字是Re entrant Lock 可重新进入的锁。
  3. 对于Synchronized而言 , 也是一个可重入锁 。 可重入锁的好处在于可以一定程度的避免死锁 。

    synchronized void setA() throws Exception{    Thread.sleep(1000);    setB();}synchronized void setB() throws Exception{    Thread.sleep(1000);}
    • 上面的代码可重入锁的一个特点 , 如果不是可重入锁的话 ,setB可能不会被当做线程执行 , 可能会造成死锁 。

三、 独享锁/共享锁

  1. 独享锁是指该所一次只能被一个线程所持有 。
  2. 共享锁是指一个锁可以同时被多个线程所持有
  3. 对于Java的ReentrantLock而言 , 是独享锁 , 但对于Lock的另一个实现类ReadWriteLock , 他的读锁是共享锁 , 写锁是独享锁 。
  4. 读锁的共享性可以保证并发读是非常高效的 ,,读写, 写读, 写写是互斥的。
  5. 独享锁与共享锁也是通过ASQ来实现的 , 通过不同的方法 , 来实现独享或者共享 。
  6. 对于Synchronized而言 , 是独享锁 。

四、 互斥锁/读写锁

  1. 独享锁/共享锁是一种广义的说法 , 互斥锁/读写锁是一种具体的实现 。
  2. 互斥锁在java中的具体实现是ReentrantLock
  3. 读写锁在java中的具体实现是ReenWriteLock

五、乐观锁/悲观锁

  1. 乐观锁与悲观锁不是指具体的什么类型的锁 , 而是值看待并发同步的角度 。
  2. 悲观锁认为对于同一数据的并发操作 , 一定会发生修改 , 即使是真的没有修改, 也会认为修改(哈哈哈哈哈哈) 。 因此对于同一数据的并发操作 , 悲观锁采取加锁的形式。 悲观的 认为 , 不加锁的并发操作一定会出现问题 。
  3. 乐观锁则认为对于同一数据的并发操作 , 是不会发生修改的 。 在更新数据的时候 , 会采用尝试更新 , 不断重复的方式更新数据 。 乐观的认为 , 不加锁的并发操作是没有问题的 。
  4. 从上面的描述可以看出 , 悲观锁更适合写操作非常多的场景 , 乐观苏更适合于读操作非常多的场景 , 不加锁会带来非常大的性能提升 。
  5. 悲观锁的体现就是在java中使用各种锁 。
  6. 乐观锁的体现就是在java中不使用锁 , 常常采用CAS算法 , 典型的列子就是原子类 , 通过CAS自旋实现原子操作的更新 。

六、分段锁

  1. 分段锁是一种所得设计 , 而不是一种具体的锁 。 对于ConcurrentHashMap而言 , 其并发的实现就是铜鼓分段锁的形式来实现高校的并发操作 。
  2. 在ConcurrentHashMap中的分段锁称为Segment , 它类似于HashMap(JDK7和8中的实现)的结构 。 即内部有一个Entry数组 , 数组中的每个元素又是一个链表 , 同时又是一个ReentrantLock(Segment继承了ReentrantLock)。当需要put元素的时候 , 并不是对整个hashmap进行加锁 , 而是通过hashcode来知道他要放到哪一个分段中 , 然后对那个分段进行加锁 ,所以当多线程put时 , 只要不是放在一个分段中 , 就实现了真正的并行插入 。 但是在统计size的时候 , 也就是获取hashmap的全局信息的时候 , 就需要获取所有的分段锁才能统计。
  3. 分段锁的设计是为了细化锁的粒度 , 当操作不需要更新整个数组的时候 , 就仅仅针对数组中的一项进行加锁操作 。

七、偏向锁/轻量级锁/重量级锁

  1. 这三种锁是指锁的状态 , 并且是针对Synchronized 。 在java5中通过引入锁升级的机制来实现高效Synchronized 。 这三种锁的状态是对象监视器在对象头中的字段来表明的 。
  2. 偏向锁是指一段同步代码一直被一个线程访问时, 那么线程就会自动获取锁 ,降低获取锁的代价 。
  3. 轻量级锁是指当锁是偏向锁的时候 , 被另一个线程所访问 , 偏向锁就会升级为轻量级锁 ,其他线程会通过自旋的形式获取锁 , 不会阻塞提高性能 。
  4. 重量级锁是指当锁为轻量级锁的时候 , 另一个线程虽然是自旋 , 但是自旋不会一直持续下去 ,当自旋到一定次数 , 还没获取到锁时 , 就会进入阻塞 , 该锁膨胀为重量级锁 。 重量级锁会让其他申请锁的线程进入阻塞 , 性能降低 。

八、自旋锁

  1. 在java中自旋锁是指尝试获取锁的线程不会被立即阻塞 , 而是采用循环的方式去尝试获取锁 , 这样的好处是减少线程上下文切换的消耗 , 缺点是循环会消耗CPU 。

参考某位大神的博客 , 在这里感谢大神 嘻嘻嘻~~~ 参考地址:https://www.cnblogs.com/qifengshi/p/6831055.html

原创粉丝点击