java线程安全和锁优化

什么是java语言中的线程安全

当多个线程访问同一段代码时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者再去调用方法进行任何协调操作,调用这段代码的行为都可以保证获得正确的结果,那这段代码就是线程安全的!
上述安全的描述要求 代码段封装了所有必要的正确性保障手段,使得其不存在多线程访问带来的问题一样。

线程安全是针对多个线程间存在共享变量的情况!没有共享,就不用谈了

java线程的安全级别

1、不可变immutable

不可变的对象,一定是线程安全的,无需安全保障代码:final 修饰的基本数据类型,String类,枚举类型,数值包装器类,BigInteger BigDecimal

2、绝对线程安全

就跟不存在多线程带来的问题一样, 大多数都不是绝对安全的

3、相对线程安全  通常意义上的线程安全

保证对对象的单次操作是线程安全的,而一系列顺序操作还是得加同步代码保障
jdk中标注为线程安全的类型几乎都是,如vector  hashtable等

4、线程兼容

对象或类型的单次操作就不是线程安全的,但是可以通过正确使用同步代码来保障
jdk中绝大部分类、代码块 都是线程兼容式,否则没法搞多线程了

5、线程对立  排斥多线程

不管是否有同步代码的保障,都无法保障正确性

线程安全的实现方法

目的:同步 

1、互斥,阻塞同步

synchronized块,悲观并发策略

  • 在同步块前后生成 monitorenter和monitorexit两个字节码指令,锁定特定的对象,或者是方法关联的 实例对象 或  Class 对象(类方法时)。
  • 同步块对同一线程是可重入的,不会自己把自己锁死
  • 同步块执行时会阻塞其他线程的进入
  • java线程是映射到操作系统的轻量级进程的,切换线程意味着切换用户态到内核态,切换过程就很耗处理器时间
  • 重量级的同步关键字

重入锁J.U.C的java.util.concurrent.ReentrantLock

  • api级别的,可实现高级功能
  • 等待线程,超时时可中断:放弃等待或去处理其他任务
  • 公平锁:按申请顺序依次获得
  • 锁可绑定多个条件:可多次调用newCondtion来添加锁时必须符合的 Condition

优先考虑使用synchronized,性能已经相差无几


2、非阻塞同步

基于冲突检测的乐观并发策略:先进行操作,在进行检测,有冲突时进行补救,否则就成功啦!
非阻塞:上面的步骤无需将线程挂起

上面两步必须是原子的,这个得靠硬件指令集的原生支持:保证从语义上是多次操作的行为只通过一条处理器指令就能完成。
这样的指令现代cpu一般提供为:

  • 测试并设置:Test-and-Set
  • 获取并增加:Fetch-and-Increment
  • 交换:        Swap
  • 比较并交换:Compare-and-Swap CAS,如果符合预期值则更新,否则不更新
  • 加载链接、条件存储:Load-Linked/store-condition , LL/SC

可以通过J.U.C的一些原子类的 compareAndSet   compareAndIncrement来 间接使用 CAS

CAS 有所谓的ABA问题即变量值曾经有其他值,但最后还是恢复原值,解决办法:添加版本信息

3、无同步方案


  • 可重入代码,任何时候都可以中断,返回后再处理 结果不会错
    那些根据输入参数,能返回固定结果的 代码


  • 线程本地存储ThreadLocal

http://www.iteye.com/topic/103804

http://www.iteye.com/topic/777716

适用前提:对共享变量的操作可以 都归结到 一个线程之内

1.Thread类中有一个成员变量叫做ThreadLocalMap,它是一个Map,他的Key是ThreadLocal类
2.每个线程拥有自己的申明为ThreadLocal类型的变量,所以这个类的名字叫'ThreadLocal':线程自己的(变量)
3.此变量生命周期是由该线程决定的,开始于第一次初始(get或者set方法)
4.由ThreadLocal的工作原理决定了:每个线程独自拥有一个变量,并非共享或者拷贝
5.ThreadLoacl变量的活动范围为某线程,并且我的理解是该线程“专有的,独自霸占”,对该变量的所有操作均有该线程完成!也就是说,ThreadLocal不是用来解决共享,竞争问题的。

不能太多,因为耗内存

JVM中的锁优化

自旋锁与自适应自旋

  • 一个线程请求共享数据的锁时如果已经被其他线程锁住了,则一般会执行挂起,然后恢复。
  • 但其实锁定状态持续的时间会很短,所以可以让那个线程稍微等一下,不放弃当前的处理器,不必挂起和恢复
  • 执行一个忙循环(自旋)来等待,看看锁是否已经可得,因为这两个动作要os介入,耗时耗资。
  • 这个自旋等待时间是有限的,过期了就应该被挂起。
  • 根据自旋等待成功的几率,自适应 的修改 自旋等待时间,几率越大,等待时间可越长。

锁消除

jvm即时编译器会将代码中有同步处理,但最后分析却没有共享数据竞争的锁 消除掉。
逃逸分析技术的支持。
jdk中有很多同步的代码

锁粗化

一系列的连续操作都对同一个对象反复加锁解锁,jvm会将锁放到这些操作序列的外部

对象头的内存字节码MardiWord,与类信息无关的运实时信息 的存储空间

32|64bit的运行时数据信息:

  • hashcode:25bit
  • gc分代年龄:4bit
  • 2bit的锁标识
  • 1bit固定为0

轻量级锁

经验假设:对于绝大部分锁,在整个同步期间都是不存在竞争的,则使用 CAS避免互斥开销

偏向锁

太难了 暂时没理解














你可能感兴趣的:(多线程,threadLocal,synchronized,锁优化)