1、线程的安全性

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

2、线程安全性的体现

(1)原子性:提供了互斥访问,同一时刻只能有一个线程对他进行操作。

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

(3)有序性:一个线程观察其他线程中的指令执行顺序,由于指令从排序的存在,该观察结果一般杂乱无序。

3、原子性

3.1Atomic包

在Atomic包里一共有12个类,四种原子更新方式,分别是原子更新基本类型,原子更新数组,原子更新引用和原子更新字段。Atomic包里的类基本都是使用Unsafe实现的包装类

swift 多线程安全 多线程的安全_有序性


下面进行一些简单的介绍,详细的话,自己去学习

AtomicInteger类:
AtomicInteger:原子更新整型,该类使用了Unsafe类(Unsafe只提供了三种CAS方法,compareAndSwapObject,compareAndSwapInt和compareAndSwapLong),所以是线程安全的。

其CAS的底层实现机制是在一个死循环中不断尝试修改目标的值,直到修改成功,在竞争不激烈的情况下,他修改成功的几率很高,否则失败的几率很高,在大量的修改失败操作后,就会进行多次的循环操作,效率就会受到一定的影响。

AtomicLong类
AtomicLong:原子更新长整型,这里还需要去了解下LongAddre类(LongAddr有多个值,可以竞争的资源多,抢到的概率大,如果抢占不到,还可能扩容.有一个base值,然后多一个值cell,高并发时用多个cell累加)

AtomicReference类
AtomicReference是作用是对"对象"进行原子操作,它是通过"volatile"和"Unsafe提供的CAS函数实现"原子操作。

(1) value是volatile类型。这保证了:当某线程修改value的值时,其他线程看到的value值都是最新的value值,即修改之后的volatile的值。
(2) 通过CAS设置value。这保证了:当某线程池通过CAS函数(如compareAndSet函数)设置value时,它的操作是原子的,即线程在操作value时不会被中断。

AtomicReferenceFieldUpdater类
AtomicReferenceFieldUpdater,是一个基于反射的工具类,它能对指定类的指定的volatile引用字段进行原子更新,该类中定义了一组抽象方法和工厂构造器。

3.2、CAS的ABA 问题

CAS问题: CPU 会将内存中将要被更改的数据与期望的值做比较。然后,当这两个值相等时,CPU 才会将内存中的数值替换为新的值。否则便不做操作。最后,CPU 会将旧的数值返回。

简单的来说,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则返回V。这是一种乐观锁的思路,它相信在它修改之前,没有其它线程去修改它;而Synchronized是一种悲观锁,它认为在它修改之前,一定会有其它线程去修改它,悲观锁效率很低。

例如:
(1)进程P1在共享变量中读到值为A
(2)P1被抢占了,进程P2执行
(3)P2把共享变量里的值从A改成了B,再改回到A,此时被P1抢占。
(4)P1回来看到共享变量里的值没有被改变,于是继续执行。

解决思路:每次变量更新的时候,把变量的版本号加一,A改成了B又改回了A的过程,就是A 版本1 B版本2 A版本3,, 从而解决ABA问题.
3.3锁
关于锁,详情请参考悲观锁
假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。悲观锁假定其他线程企图访问或者改变你正在访问、更改的对象的概率是很高的,因此在悲观锁的环境中,在你开始改变此对象之前就将该对象锁住,并且直到你提交了所作的更改之后才释放锁。

乐观锁
假设不会发生并发冲突。轻易不加锁。

synchronized:

swift 多线程安全 多线程的安全_共享变量_02


swift 多线程安全 多线程的安全_共享变量_03


4、可见性

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

(1)线程的交叉执行

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

(3)共享变量更新后的值没有在工作内存与主存间及时更新

4.2可见性-synchronized

JMM关于synchroniz的两条规定:

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

4.3可见性-volatile

swift 多线程安全 多线程的安全_悲观锁_04


swift 多线程安全 多线程的安全_共享变量_05


swift 多线程安全 多线程的安全_共享变量_06


注意:

volatile关键字的作用是强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值。

使用volition关键字增加了实例变量在多个线程间的可见性。但volition有个致命的缺点就是volatile不具有原子性,是线程不安全的

5、有序性

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

java里面可以通过volatile、synchronized、Lock来保证有序性,另外java内存模型具备一些先天的有序性(hapens-befrore原则)
5.1有序性-happens-before原则

  • 程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作。
  • 锁定规则:一个unLock操作先行发生于后面对同一个锁额lock操作。
  • volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作。
  • 传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C。
  • 线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作。
  • 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生。
  • 线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行。
  • 对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始。