zoukankan      html  css  js  c++  java
  • 线程安全与锁优化

    《深入理解Java虚拟机》第十三章

    当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的。

    线程安全强度排序:不可变,绝对线程安全,相对线程安全,线程兼容,线程对立。

    • 不可变:不可变的对象一定是线程安全的,无论是对象的方法实现还是方法的调用者,都不需要采取任何保障线程安全的措施,String和八大基础类型的包装类(没有变量的类,有变量必须final修饰的基础类型或者final修饰的不可变类)
    • 绝对线程安全:不管运行时环境如何,调用者都不需要任何额外的同步措施。
    • 相对线程安全:对象线程安全,但调用时需要额外的同步措施,这个类型就是常说的线程安全,Java中大部分的线程安全类都属于这种类型,Vector,HashTable,Collections,synchronizeCollection()方法包装的集合。
    • 线程兼容:对象线程不安全,调用时采取正确的同步手段,使其线程安全
    • 线程对立:不管采取怎样的同步措施,都不能保证线程安全。

    线程安全的实现方法

    • 互斥同步
    • 非阻塞同步
    • 无同步

    互斥同步:多个线程访问共享数据时,同一时刻共享数据仅被一个线程使用,其他线程阻塞。

    重量级锁synchronize,经过编译后代码前后monitorenter,monitorexit两个字节码指令(计数器),两个字节码指令都需要一个refrence类型的参数指明要锁定和解锁的对象。

    synchronize:有明确指定对象参数,就是指定的参数对应的对象,

                           没有明确指定参数(即用在方法上)时,实例方法锁定实例对象,类方法锁定类对象。

    synchronize:锁定对象对同一线程是可以重入的,不会出现自己把自己锁死的问题,只会阻塞其他线程。

    synchronize导致的问题:如果阻塞或唤醒一个线程,都需要操作系统来帮忙完成就会在用户态和核心态频繁切换,效率低,优化:通知操作系统阻塞时自旋等待,避免进入核心态。

    除synchronize外,重入锁ReentrantLock也可以实现同步。利用Lock类实现的。

    ReentrantLock增加了一些高级功能:

    • 等待可中断 :等待时间过长,可选择放弃,改处理其他事情。
    • 可实现公平锁:多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。默认是非公平的。
    • 锁可以绑定多个条件

    未来的jdk改进会偏向于原生的synchronize

    非阻塞同步:基于冲突检测乐观并发策略,先进行操作,如果没有其他线程争用共享数据,操作成功。有争取,产生了冲突,采取补偿措施(最常见的补偿措施:不断重试,知道成功为止)。

    乐观并发策略需要“硬件指令集的发展”才能进行,操作+冲突检测的原子性

    一条处理器指令就能完成操作

    测试并设置(Test-and-Set)

    获取并增加(Fetch-and-Increment)

    交换(Swap)

    比较并交换(Compare-and-Swap,CAS)

    加载链接、条件存储(Load-Linked/Store-Conditional,LL、SC)

    Jdk1.5之后,java程序中才可以使用CAS操作,sun.misc.Unsafe类中compareAndSwapInt和compareAndSwapLong等方法包装提供。

    Unsafe类仅启动类加载器加载的类才能执行,用户程序不能直接调用,可用反射,不然只能通过java.util.concurrent来间接调用。

    while(true){
        int current = get();
        int next = current + 1;
        if(compareAndSet(current,next)){
            return next
        }  
    }
        

    CAS操作的ABA问题:A-->B-->A,认定变量没有被线程改变过

    无同步方案

    可重入代码:纯代码,每次相同输入,返回结果相同。

    线程本地存储 : 共享数据的代码保证在同一线程中执行,可把共享数据可见范围限制在同一个线程内,ThreadLocal的ThreadLocalMap

    锁优化

    • 自旋锁自适应自旋:互斥同步时,等待线程继续消耗处理器执行时间,防止进入核心态。但等待时间长,会浪费处理器性能。
    • 锁消除:虚拟机即时编译器在运行时,对代码上同步,但检测到不可能存在共享数据竞争的锁进行消除。
    • 锁粗化:虚拟机检测,同步块锁定同一对象,多且零碎。优化到整个操作序列外面。
    • 轻量级锁:没有多线程竞争的前提下,减少传统的重量级锁所使用操作系统互斥量产生的性能消耗。两条线程争用锁,无用
    • 偏向锁:消除数据在无竞争情况下的同步原语,进一步提高程序的运行性能。单线程每次获取资源需要同步锁,偏向锁就是第一次获取偏向锁,后续如果没有其他线程同步锁,这个线程后续请求资源不需要重新获取锁
  • 相关阅读:
    Oracle创建序列,删除序列
    java base58
    百度地图 显示,定位,轮廓图
    百度地图 圈出省份轮廓图并高亮
    基于双向链表的增删改查和排序(C++实现)
    统计字母出现次数
    线程安全
    C++面试秘笈笔记
    牛客选择题刷题
    new delete 浅析
  • 原文地址:https://www.cnblogs.com/wqff-biubiu/p/10426660.html
Copyright © 2011-2022 走看看