类别 | 定义 | 描述 |
原子性 | 提供了互斥访问,同一时刻只能有一个线程对它进行操作 | - Java虚拟机对基本数据类的访问、读写都是具备原子性的(除long、double)--注意:i++操作是线程非安全的,即使数据类型的操作是原子性的,但是i++包含三个操作(read-load、assign、store-write)发生指令重排序,导致结果异常。
- CAS(compareAndSwap)来实现原子性。(读取传入对象o在内存中偏移量为offset位置的值与期望值expected作比较,成功则更新)自旋CAS
- 锁 来实现原子性操作 独占锁(Synchronized、ReentrantLock、)共享锁(ReentrantReadWriteLock、)...
|
可见性 | 一个线程对主内存的修改可以及时的被其他线程观察到 | volatile、synchronized 等都可以实现可见性 - final 不能保证可见性,final只能保证初始化完成后,final修饰的变量不能被修改,但是不代表fina域不能被多次初始化。因此 :在构造函数中,正在被构造的对象(this)没有“逸出”,那么不需要任何同步手段,就能保证任意线程看到的final域,包括基本类型和引用类型,都是已经被正确地通过构造函数初始化过了的。
- volatile 可以保证可见性,通过加入内存屏障和禁止重排序优化来实现的。volatile变量在每次被线程访问时,都强迫从主内存中重读该变量的值,而当该变量发生变化时,又会强迫线程将最新的值刷新到主内存。这样任何时刻,不同的线程总能看到该变量的最新值 (volatile 相当于 ReadLock)。
- synchronized 可以保证可见性,使用该关键字可以保证资源的互斥性。(线程解锁前,必须把共享变量的最新值刷新到主内存中;线程加锁时,将清空工作内存***享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(注意:加锁与解锁需要是同一把锁)线程解锁前对共享变量的修改在下次加锁时对其他线程可见。)
|
有序性 | 由于执行语句重排序后,重排序的这一部分没有一起执行完,就切换到了其它线程,导致的结果与预期不符的问题 | |
1.1 什么是内存屏障(Memory Barrier)?
内存屏障(memory barrier)是一个CPU指令。基本上,它是这样一条指令: a) 确保一些特定操作执行的顺序; b) 影响一些数据的可见性(可能是某些指令执行后的结果)。编译器和CPU可以在保证输出结果一样的情况下对指令重排序,使性能得到优化。插入一个内存屏障,相当于告诉CPU和编译器先于这个命令的必须先执行,后于这个命令的必须后执行。内存屏障另一个作用是强制更新一次不同CPU的缓存。例如,一个写屏障会把这个屏障前写入的数据刷新到缓存,这样任何试图读取该数据的线程将得到最新值,而不用考虑到底是被哪个cpu核心或者哪颗CPU执行的。内存屏障正是通过阻止屏障两边的指令重排序来避免编译器和硬件的不正确优化而提出的一种解决办法。