多线程
一、Synchronized
背景:多线程同时访问对象,并且要求操作相同的资源时,就会造成数据的原子性收到破坏,出现数据的不一致或者不完整,这个关键字的作用就是确保某一时刻,方法内只有一个线程,称为同步锁机制或者互斥锁机制。
几点说明:1、每个对象都有一个 monitor (锁标记),当线程拥有这个锁标记时才能访问这个资源,没有锁标记便进入锁池。一个mointor只能分配一个线程。
2、类的每个对象都有自己的对象级别的锁,当线程要访问Synchronized修饰的方法或者代码块的时候,即拥有了这个对象级别锁,其他线程再来访问的时候,线程会被阻塞,等待,直到释放;
3、当一个线程 A 持有一个对象级别锁(即进入了 synchronized 修饰的代码块或方法中)时,线程也有可能被交换出去,此时线程 B 有可能获取执行该对象中代码的时间,但它只能执行非同步代码(没有用 synchronized 修饰),当执行到同步代码时,便会被阻塞,此时可能线程规划器又让 A 线程运行,A 线程继续持有对象级别锁,当 A 线程退出同步代码时(即释放了对象级别锁),如果 B 线程此时再运行,便会获得该对象级别锁,从而执行 synchronized 中的代码。
二、Volatile
背景:在当前的 Java 内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成数据的不一致。
要解决这个问题,就需要把变量声明为 volatile,这就指示 JVM,这个变量是不稳定的,每次使用它都到主存中进行读取。一般说来,多任务环境下,各任务间共享的变量都应该加 volatile 修饰符。
Volatile时弱化版的同步机制,在执行Volatile变量的时候,不会加锁也不会造成线程阻塞。
使用建议:在两个或者更多的线程需要访问的成员变量上使用 volatile。当要访问的变量已在 synchronized 代码块中,或者为常量时,没必要使用 volatile。
由于使用 volatile 屏蔽掉了 JVM 中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。