1. 加锁与可见性
内置锁可以用于确保某个线程以一种可预测的方式来查看另一线程的执行结果。
当线程A执行某个同步代码块时,线程B随后进入由同一个锁保护的同步代码块,在这种情况下可以保证,在释放锁之前,A看到的变量值在B获得锁后同样可以由B看到。即当线程B执行由锁保护的同步代码块时,可以知道线程A之前在同一个同步代码块中的所有操作结果。这也符合Happen-before原则,即A Happen-before B。
基于此我们在访问某个共享且可变的的变量时要求所有线程在同一锁上同步,就是为了确保写入该变量的值对于其他线程而言都是可见的,否则,如果一个线程在未持有正确锁的情况下读取某个变量,那么读到的可能是一个失效值。
加锁的含义并不仅仅局限于互斥行为,还包括内存可见性。为了确保所有线程都能看到共享变量的最新值,所有执行读操作或者写操作的线程都必须在同一个锁上同步。
2. Volatile变量
Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量为共享变量,因此不会将该变量上的操作与其他内存操作一起重新排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量的时候总会返回最新写入的值。
在访问volatile变量时不会执行加锁操作,因此也就不会执行线程阻塞,因此volatile变量是一种比sychronized关键字更轻量级的同步机制。
使用volatile变量的条件:
- 对变量的写入操作不依赖于变量的当前值,或者你能确保只有单个线程更新变量的值。
- 该变量不会与其他状态变量一起纳入不变性条件中。
- 在访问变量时不需要加锁。
总结:加锁机制既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性。