Volatile介绍
标签: 多线程
volatile的特点
1.使变量在多个线程中可见.即,使多个线程从公共堆栈中获得变量值。
2.volatile只用于修饰变量.synchronized可以修饰方法,变量和类.
3.volatile不能保证原子性. 它唯一的作用就是 使 私有线程 从 公共堆栈中获取数据。仅仅保证读取的数据最新.
4.多线程访问volatile不会阻塞,而synchronized会导致阻塞.
5.synchronized 具有 互斥性 和 可见性。 即,使用synchronized有volatile的可见性的效果.
下面用两个例子说明volatile的特点
增加变量可见性
public class VolatileDemo {
public static void main(String[] args) {
ServicePart servicePart = new ServicePart();
VolatileThreadOne one = new VolatileThreadOne();
VolatileThreadTwo two = new VolatileThreadTwo();
one.setService(servicePart);
two.setServicePart(servicePart);
Thread t1 = new Thread(one);
Thread t2 = new Thread(two);
t1.start();
t2.start();
}
}
class ServicePart {
//加入volatile修饰之后,不同线程获取flag值时,会从公共堆栈中获取,即,最新的值.
//即,当t2线程设置的flag的值为false,t1线程会马上知道flag的值为flase.
// 否则,可能会导致,t1线程一直在其私有数据栈中 获取flag的值,即,一直为true.
volatile private boolean flag = true;
public void setFlag(boolean flag) {
this.flag = flag;
}
public void doHomework() {
while (flag) {
System.out.println("我一直在,根本停不下来");
}
System.out.println("停停停");
}
}
class VolatileThreadOne implements Runnable{
ServicePart ServicePart;
public void setService(ServicePart ServicePart) {
this.ServicePart = ServicePart;
}
@Override
public void run() {
this.ServicePart.doHomework();
}
}
class VolatileThreadTwo implements Runnable{
ServicePart servicePart;
public void setServicePart(ServicePart servicePart) {
this.servicePart = servicePart;
}
@Override
public void run() {
this.servicePart.setFlag(false);
}
}
证明 volatile 不具有原子性
/**
* 这个主要证明 volatile 不具有原子性.
* 即: 会出现多线程 异步操作,出现脏读.
*
* 在方法中,原子类方法执行顺序随机.
*
* 控制台输出顺序乱序,即异步操作.
*/
/**
* Created by static-mkk on 10/4/2018.
*/
public class VolatileDemoTwo {
public static void main(String[] args) {
VolatileService volatileService = new VolatileService();
VolatileDemoTwoThreadOne t1 = new VolatileDemoTwoThreadOne();
t1.setVolatileService(volatileService);
VolatileDemoTwoThreadTwo t2 = new VolatileDemoTwoThreadTwo();
t2.setVolatileService(volatileService);
t1.start();
t2.start();
}
}
class VolatileService{
volatile public int number;
public void sayNumber(){
while(number < 1000){
number++;
if(number%50==0){
System.out.println(number);
}
}
}
}
class VolatileDemoTwoThreadOne extends Thread{
VolatileService volatileService;
public void setVolatileService(VolatileService volatileService) {
this.volatileService = volatileService;
}
@Override
public void run() {
super.run();
volatileService.sayNumber();
}
}
class VolatileDemoTwoThreadTwo extends Thread{
VolatileService volatileService;
public void setVolatileService(VolatileService volatileService) {
this.volatileService = volatileService;
}
@Override
public void run() {
super.run();
volatileService.sayNumber();
}
}
tips
:如果您用IDE来运行带代码,请把JVM的设置为-server模式,该模式下,为了线程执行效率,线程一直在私有堆栈中获取数据。导致flag一直为true.所以,虽然另外的线程把flag设置成了true,只是公共堆栈中flag设置成功,但是私有堆栈中flag依旧没变。
所以此时需要用volatile来从公共堆栈中读取最新数据。这也是volatile的作用。唯一作用。