Java多线程(一)之Java线程安全性

定义:

         当多个线程访问某个类时,不管运行环境采用何种调度方式或者这些线程如何交替执行,并且在主调代码中不需要任何额外的同步或协调,这个类都能表现出正确的行为,那么这个类是线程安全的。

线程安全三要素:

1、原子性:提供互斥访问,同一时刻只能有一个线程对它进行操作;

2、可见性:一个线程对主内存的修改可以及时的被其他线程观察到;

3、有序性:即程序的执行顺序按照代码的先后顺序执行(实际会发生指令重排,但不影响最终结果)

 

1.原子性

   分两个方面:CAS atomic包、锁sychronized和lock

   原子性-atomic包:(研究不深,只供参考)

       AtomicXXX:CAS、Unsafe.compareAndSwapInt;

       AtomicLong、LongAdder;

       AtomicReference、AtomicReferenceFieldUpdater;

      AtomicStampReference;

  CAS(compare and swap 乐观锁)

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。

CAS虽然很高效的解决原子操作,但是CAS仍然存在三大问题。ABA问题,循环时间长开销大和只能保证一个共享变量的原子操作。

  1. ABA问题。因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法作用是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
  2. 循环时间长开销大。自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。
  3. 只能保证一个共享变量的原子操作。当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij。从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作

锁sychronized和lock:

    锁机制保证了只有获得锁的线程才能够操作锁定的内存区域。

     synchronized:不可中断锁,依赖JVM,不需要自己释放锁,值得注意的是synchronized不那么重了,竞争不激烈的情况他都能解决,甚至比lock性能还好;

    对于普通方法,锁的是当前实例对象,作用范围为当前方法内;

    对于静态同步方法,锁的是当前类的Class对象,作用范围为整个静态方法;

    对于同步方法块,锁是Synchronized括号里的对象,作用范围为代码块的范围。

    lock:可中断锁,需要手动解锁,功能强大灵活,竞争激烈时也能维持常态;

     Atomic:竞争激烈也是维持常态,比lock性能好,但只能同步一个值。

 

2、可见性

               一个线程对主内存的修改可以及时的被其他线程观察到;

      导致共享变量在线程间不可见的原因:

       1、线程交叉执行

       2、重排序结合线程交叉执行

       3、共享变量更新后的值没有及时工作内存和主内存之间更新

     可见性-----synchronized

      jvm关于synchronized的两条规定:

          1、线程解锁前,必须把共享变量的最新值刷新到主内存;

          2、线程加锁时,将清空工作内存中共享变量中的值,从而使用共享变量时需要从主内存中重新读取最新的值(注意加锁和解锁是同一把锁)

      volatile 需注意:进行加操作不具有线程安全性。适合做标记量;


 

3、有序性

          Java内存模型中,允许编译器和处理器对指令进行重排序,但重排序过程不会影响单线程程序的执行,却会影响多线程并发执行的正确性。

         保证有序性:volatile、synchronized、lock

         happen-before原则:

          1、程序次序原则:按照代码书写顺序执行;虚拟机可能会对 不影响执行结果的指令 重排。

          2、锁定规则:unlock操作先行发生于后面对同一个锁的lock操作,必须对前一个锁解锁才能加锁。

          3、volatile变量规则:对一个数量的写操作先行发生于后面对这个变量的读操作。

          4、传递规则:如果操作A先行发生于操作B,操作B先行发生于操作C,则操作A先行发生于操作C;

          5、 线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作;

          6、线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生;

          7、线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行;

          8、 对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值