zoukankan      html  css  js  c++  java
  • java中的锁

    这是从网上找的一张图,感觉脉络很清晰,就顺着这张图进行讲解java中锁的分类

    Java中的锁有哪些种类?

    • 公平锁/和非公平锁
    • 乐观锁/悲观锁
    • 独占锁/共享锁
    • 自旋锁
    • 可重入锁
    • 互斥锁/读写锁
    • 偏向锁/轻量级锁/重量级锁
    • 分段锁
    • 分布式锁

    以上有很多所得名词,这些状态不是全指锁的状态,有的是锁的特性,有的是所得设计

    1)公平锁/非公平锁

    公平锁:是指多个线程按照申请锁的顺序来获取锁

    非公平锁:是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程闲获得锁,有可能会造成优先级反转或者饥饿现象

    对于Java ReentrantLock而言,通过构造函数指定锁是否是公平锁,默认是非公平锁.非公平锁的优点在于吞吐量比公平锁的大

    public ReentrantLock() {
            sync = new NonfairSync();
        }

    对于Synchronized而言,也是一种非公平锁,由于其并不像ReentrantLock 是通过AQS来实现的线程调度,所以没有办法使其变成公平锁

    2)乐观锁/悲观锁

    乐观锁与悲观锁不是指具体的什么类型锁,而是指看待并发同步的角度 

    悲观锁认为对于同一数据的并发操作,一定会是修改的,哪怕没有修改,也会认为修改,因此对于同一个数据的并发操作,悲观锁采用的加锁的形式

    乐观锁:则与之相反,认为对于同一个数据的并发操作,是不会发生修改的,在更新数据的时候,会采用不断更新,不断获取更新的数据

    悲观锁:在java中的使用,就是各种锁

    乐观锁:在java中,是无锁编程,长采用的CAS算法,典型的原子类,通过CAS自旋实现原子操作的更新

    在mysql中乐观锁和悲观锁的具体实现,其中悲观锁,主要是表锁,行锁以及间隙锁  叶锁  读锁 ,因为这些所在触发的时候必然引起线程阻塞,所以叫悲观锁,另外乐观锁在mysql中本身并不存在的,但是mysql提供了mvcc的机制,支持乐观锁机制 

    mvcc机制:多版本并发控制,保证数据操作在多线程过程中,保证事务隔离的机制,可以降低锁竞争的压力,保证高并发量,在这个过程中,每开启一个事务,会生成一个事务的版本号,被操作的数据会生成一个事务的版本号,被操作的数据会生成一条新的(临时)数据行,但是提交之前是对其他事务是不可见的,对于数据的更新成功,会将这个版本号更新到数据行中,事务提交成功,将新的版本号,更新到此数据行中,这样保证每个事物操作的数据,都是相互不影响的,不存在锁的问题

    3)独享锁/共享锁

       独享锁是指该锁一次只能被一个线程所持有

       共享锁是指该锁可以被多个线程所持有

    对于Java ReetrantLock而言,是独享锁,但是对于实现类是ReadWriteLock,其读锁是共享锁,其写是独享锁,独锁的共享锁可以保证并发读是非常高效的,读写,写读,写写的过程是互斥的,独享锁和共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享

      

    4)自旋锁

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

    可重入锁

    又名递归锁,,是指在同一个线程在外层获取方法获取锁的时候,在进入内层方法会自动获取锁

    对于Java  ReetrantLock 而言,从名字可以看出是一个可重入锁,对于synchjronized 而言,也是一个可重入锁,可重入锁的一个好处是可一定程度上避免死锁

    synchronized void setA() throws Exception{
        Thread.sleep(1000);
        setB();
    }
    
    synchronized void setB() throws Exception{
        Thread.sleep(1000);
    }

    上面的代码就是一个可重入锁的一个特点,如果不是可重入锁的话,setB可能不会被当前线程执行,可能造成死锁。

    5)互斥锁/读写锁

    上面讲的独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。
    互斥锁在Java中的具体实现就是 ReentrantLock
    读写锁在Java中的具体实现就是 ReadWriteLock

    6)偏向锁/轻量级锁/重量级锁

    这三种锁是指锁的状态,并且是针对Synchronized  ,在java 5 通过引入锁升级的机制,来实现高效Synchronized,其中锁的状态是通过对象监视器在对象头中的字段表明的.

    偏向锁是指一段同步代码一直被一个线程访问,哪么该线程会自动获取锁,降低获取锁的代价

    轻量级锁是指当前锁是偏向锁的时候,被另一个线程访问,偏向锁就会升级为轻量级锁,其他线程通过自旋的形式获取锁,不会阻塞,提高性能

    重量级锁是指在当前锁是轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁,重量级锁会让其他申请的线程进入阻塞,性能降低

    7)分段锁

      分段锁,我们并不陌生,其实是一种锁的设计,对于ConCurrrentHashMap 而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作

      我们以ConcurrentHashMap 来说一下分段锁的含义和设计思想,ConcurrentHashMap中的分段锁被称为Segment,,他类似于HashMap(1.8以前)的结构,即内部拥有一个Entry的数组,而数组中的每一个元素就是一个链表;同时又继承ReentrantLock,当需要put元素的时候,并不是对整个hashmap进行加锁,而是通过hashMap来知道他要放入哪个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就可以实现真正的并行插入,但是在统计size的时候,可就是获取hashMap的全局信息,就需要获取所有分段锁下能统计.分段锁的设计目的是细化锁的粒度

    8)分布式锁

    提到分布式锁,不由想到CAP理论,重新回顾一下这个理论,任何一个分布式系统都无法同时满足一致性(Consistency)  可用性(Availability)  和分区容错性(Partition tolerance),最多只能满足两项,所以系统在设计之初就要对三者做出取舍,在互联网领域的绝大多数的场景中,都需要牺牲一致性来换取系统的高可用性,系统只需保证"最终一致性",只要这个最终时间用户可以接受即可

    在很多场景中,我们为了保证数据的最终一致性,需要很多解决方案,比如部分布式事务,分布式锁.针对分布式锁的实现,目前比较常用的有一下几种解决方案

    • 基于数据库实现分布式锁
    • 基于缓存(Redis,memcached,tair)实现分布式锁
    • 基于Zookeeper实现分布式锁
  • 相关阅读:
    【python】 time模块和datetime模块详解 【转】
    【python 】装饰器 (多个参数的函数,带参数的装饰器)【转】
    从测试角度对测试驱动开发的思考【转】
    mysql性能优化-慢查询分析、优化索引和配置【转】
    【python 】Requests 库学习笔记
    二本院校计算机专业考研上岸985
    mysql创建触发器
    pat 1134 Vertex Cover (25分) 超时问题
    数据库三级封锁协议简述
    pat 1139 First Contact (30分) 题目详解
  • 原文地址:https://www.cnblogs.com/developerxiaofeng/p/9224948.html
Copyright © 2011-2022 走看看