zoukankan      html  css  js  c++  java
  • 怎样才是线程安全的?

    一、final 一下

    final 关键字,具有不可变性。所以并发中对于其的使用时线程安全的。

    final 可以修饰变量,方法及类对象。

    不可变:不可被修改,不可被继承,不可被覆盖,重写等。

    二、加锁

    锁的本质就是一个同步块,处于同步块儿中的操作可以认为是线程安全的。

    那怎么加锁呢?

    1、synchronized 关键字

    底层其实对应的是用monitorenter及monitorexit字节码指令包裹需要同步的执行。

    monitorenter

    ... ...

    monitorexit 

    同一个线程对同一个同步块的加锁,只会执行一次,当获取到锁后,再次进入同步块时,则不需要再次进行加锁,我们称之为“可重入”,当然,重入也会被记录,

    线程对对象的加锁标识,存在于对象的头信息中,具体可以了解下:偏向锁、轻量锁、重量所,锁膨胀相关知识。

    对于静态执行的锁:锁类对象。

    对于费静态执行的锁:锁类实例。

    2、lock:

    我们称之为可重入锁。

    可重入,也就是我们上面所说的,在获锁期间,可以重复进入。

    相对于synchronized来说,lock更加灵活:可中断,可超时,公平锁等。

    lock的实现基于AQS。

    基本使用:

    try {

    lock();

    ... ...

    } finally {

        unlock();

    }

    特别需要注意的一点是一定要保证unlock。

    三、局部变量

    局部变量,其实实操对应的是线程私有栈中的变量操作。因为私有,所以是线程安全的。

    线程栈具体参考:Java虚拟机栈 

    局部变量以栈帧的形态参与实际的运行计算。

    四、锁优化

    1、CAS自旋:

    CAS:Compare And Swap 对比然后交换值,CAS 属于硬件指令级别,因此本身可以保证原子性。

    自旋:CAS操作中会有个期望值的限制,达不到这个预期,则执行不成功。因此期望最终能够执行,则需要多次尝试,也就是所谓的“自旋”,循环尝试。

    例如,对于普通的“++”操作,是非线程安全的,而要达到线程安全的+1目的,就必须添加必要的同步性保障,或者,使用此处我们所论述的CAS特性。

    示例:AtomicInteger::getAndIncrement

    内部通过 Unsafe 实例的 compareAndSwapInt 方法实现。

    2、轻量锁

    单一线程访问加锁优化;对象头信息 Mark Word;前提---对于绝大多数锁,在整个同步周期内是不存在竞争的;无竞争情况下,使用CAS操作消除同步使用的互斥量。

    执行过程:
     
    进入同步块,对象未加锁(锁标志位 01)
    |
    线程栈帧中建立一个所记录(Lock Record)空间(存储锁对象Mark Word 拷贝:Displaced Mark Word)
    |
    虚拟机尝试使用CAS操作,将对象的Mark Word更新为指向Lock Reord的指针
    |
    更新成功,线程拥有对象锁,对象Mark Word锁标志位变为 00
    |
    更新失败,则检查对象Mark Word是否指向当前线程
    |
    是的话,则说明已经拥有了轻量级锁,继续同步块操作
    |
    否的话,则说明对象已经被其它线程占有,两个及以上线程征用同一个锁,则膨胀为重量级锁,所标志位变为10,Mark Word存储的是指向重量级锁()的指针
     
    解锁:CAS 将线程的复制的Displace Mark Word和对象的Mark World替换回来。

    3、偏向锁

    无竞争情况下,消除整个同步块。偏向于第一个获得锁的线程。

    对象锁第一次被线程获取的时候,虚拟机把对象头的标志位设为01,偏向模式,同时使用CAS模式把获取到这个锁的线程ID写入对象的Mark Word中
    |
    获取锁成功,持有偏向锁的线程每次进入锁的相关同步块,虚拟机都可以不再进行任何同步操作。
    |
    另外线程尝试获取此所,则偏向模式结束,撤下偏向(Revoke Bias),恢复未锁定(00)或者轻量级锁定(10)。

    4、锁粗化

    选择合适的粒度层级进行加锁同步。

    5、锁消除

    如果一段代码中,堆上的所有数据都不会逃逸出去而被其它线程访问到,当作栈上数据对待,消除锁。

    等等。

  • 相关阅读:
    给Lisp程序员的Python简介
    QuickLisp常用命令
    修改windows cmd字体
    Eclipse生成JavaDoc时指定字符集, 避免页面乱码
    css颜色名
    css3动画
    使用placeholder实现动态配置persistence.xml
    spring下配置tomcat jdbc pool 报找不到"com.mysql.jdbc.Driver"类
    去除移动端浏览器点击元素时的边框
    JS经典闭包问题
  • 原文地址:https://www.cnblogs.com/niejunlei/p/12863360.html
Copyright © 2011-2022 走看看