原子性:
操作要么都执行,要么都不执行。
比如:A原有5块石头,B原有3块石头;现有如下操作:
A让C给予B一块石头,那么应该发生的事情有,A失去一块石头,变为4块,B得到一块石头变为5块;此时交易成功。
不排除有意外情况,比如C在给予B的过程中,B出门了,那么,我们称这个操作失败了,要进行回滚。回滚就是回到事务开始之前的状态,
A还是5块石头,B还是4块石头。
我们把这种要么一起成功(A成功减少一块石头,同时B得到一块石头),要么一起失败(A回到原来状态,B也回到原来状态)的操作叫原子性操作。
如果把一个事务可看作是一个程序,它要么完整的被执行,要么完全不执行。这种特性就叫原子性
i=1+1是原子性操作,i++不是,因为i++实际上分成了3步操作①从内存中读取出变量 i 的值②将 i 的值加1③将 加1 后的值写回内存,这个期间可能其他线程修改了i的值,所以不保证i++的结果=2
可见性:
Java内存的规定:
-线程对共享变量的所有操作都必须在自己的工作内存中进行,不可直接从主内存中读写; -不同线程之间无法直接访问其他线程工作内存中的变量,线程间的变量值的传递需要通过主内存,子线程中的都是主线程变量的副本。如果不加volatile或synchronized,那么子线程中对副本的操作结果将不能刷到主内存中
volatile 与 synchronized 的比较
volatile主要用在多个线程感知实例变量被更改了的场合,从而使得各个线程获得最新的值。它强制线程每次从主内存中讲到变量,而不是从线程的私有内存中读取变量,从而保证了数据的可见性。
关于synchronized,可参考:JAVA多线程之Synchronized关键字–对象锁的特点
比较:
①volatile轻量级,只能修饰变量。synchronized重量级,还可修饰方法
②volatile只能保证数据的可见性,不能用来同步,因为多个线程并发访问volatile修饰的变量不会阻塞。
synchronized不仅保证可见性,而且还保证原子性,因为,只有获得了锁的线程才能进入临界区,从而保证临界区中的所有语句都全部执行。多个线程争抢synchronized锁对象时,会出现阻塞。
线程安全性
线程安全性包括两个方面,①可见性。②原子性。
从上面自增的例子中可以看出:仅仅使用volatile并不能保证线程安全性。而synchronized则可实现线程的安全性。