[TOC]

原子性

即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,是不可被中断的。

例如:

a = 233;        //语句1
b = a; //语句2
a++; //语句3
a = a + 1; //语句4

其实只有语句1是原子性操作,其他三个语句都不是原子性操作。

a++和 a = a+1包括3个操作:读取x的值,进行加1操作,写入新的值。

Java内存模型只提供了基本的读取和赋值原子操作保证,若要对大范围的操作提供原子保证,可以通过synchronized和Lock来实现,来达到线程安全。

可见性:

可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

对于可见性,Java提供了volatile关键字来保证可见性。

volatile关键字

volatile保证了不同线程对这个变量进行操作时的可见性

1)一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的(拷贝副本》修改副本》刷新回主存);

2)禁止进行指令重排序;

举例:

//线程1:
result = init(); //语句1
flag = true; //语句2

//线程2:
while(!flag ){
Thread.sleep();
}
doSomething(result);

若flag未加volatile 修饰,可能先于语句1执行,在多线程环境下,result还未取到结果,可flag已为true,线程2误以为result有值,便开始执行doSomething…,最后发生异常~

除此之外,常见用volatile 关键字的地方还有双重判空的单例模式;

因 volatile关键字会在内存中产生一个内存屏障,起到阻挡作用,禁止了指令的重排。

synchronized和Lock

另外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。

有序性

即程序执行的顺序按照代码的先后顺序执行。

在Java里面,可以通过volatile关键字来保证一定的“有序性”;但并不是100%,毕竟volatile无法保证操作的原子性;

synchronized关键字通过加锁可以使线程达到同步,但是会产生阻塞,涉及到用户模式内核模式的转换,这会很影响程序执行的效率。那么存在不用加锁的吗?当然有—无锁机制CAS,原子类的实现原理,放在后面讲解,这里不做讨论。

通过使用以及原理对比,我们可以发现,volatile关键字在某些情况下性能要优于synchronized,但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性!

happens-before原则

摘自—-《深入理解Java虚拟机》

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