首页 > 编程语言 >java之用volatile和不用volatile的区别

java之用volatile和不用volatile的区别

时间:2023-04-26 20:01:43浏览次数:37  
标签:java Pattern 之用 bChanged static private volatile public


在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成数据的不一致。 




要解决这个问题,只需要像在本程序中的这样,把该变量声明为volatile(不稳定的)即可,这就指示JVM,这个变量是不稳定的,每次使用它都到主存中进行读取。一般说来,多任务环境下各任务间共享的标志都应该加volatile修饰。 




Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。 


用volatile和不用volatile的区别,运行一下,就知道了。

不用volatile:

package com.keyword;

public class TestWithoutVolatile {
	private static boolean bChanged;

	public static void main(String[] args) throws InterruptedException {
		new Thread() {

			@Override
			public void run() {
				for (;;) {
					if (bChanged == !bChanged) {
						System.out.println("!=");
						System.exit(0);
					}
				}
			}
		}.start();
		Thread.sleep(1);
		new Thread() {

			@Override
			public void run() {
				for (;;) {
					bChanged = !bChanged;
				}
			}
		}.start();
	}

}



运行后,程序进入死循环了,一直在运行。


用volatile:

package com.keyword;

public class TestWithVolatile {
	private static volatile boolean bChanged;

	public static void main(String[] args) throws InterruptedException {
		new Thread() {

			@Override
			public void run() {
				for (;;) {
					if (bChanged == !bChanged) {
						System.out.println("!=");
						System.exit(0);
					}
				}
			}
		}.start();
		Thread.sleep(1);
		new Thread() {

			@Override
			public void run() {
				for (;;) {
					bChanged = !bChanged;
				}
			}
		}.start();
	}

}



程序输出!=,然后马上退出。

但是,很多情况下,用不用volatile,感觉不出什么区别,什么时候要用volatile呢?看看JDK里使用volatile的类。

比如java.util.regex.Pattern里的变量:

private transient volatile boolean compiled = false;



还有,java.lang.System的变量:

private static volatile Console cons = null;



一般就是初始化的时候,需要用到volatile。

java.util.Scanner里的变量,如:

private static volatile Pattern boolPattern;
private static volatile Pattern separatorPattern;
private static volatile Pattern linePattern;



初始化boolPattern的代码:

private static Pattern boolPattern() {
        Pattern bp = boolPattern;
        if (bp == null)
            boolPattern = bp = Pattern.compile(BOOLEAN_PATTERN,
                                          Pattern.CASE_INSENSITIVE);
        return bp;
}



上面的情况,可以使用synchronized来对boolPattern加锁,但是synchronized开销比volatile大,volatile能够胜任上面的工作。


volatile不保证原子操作,所以,很容易读到脏数据。


使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。 



标签:java,Pattern,之用,bChanged,static,private,volatile,public
From: https://blog.51cto.com/u_1002776/6228784

相关文章

  • java流程控制
    scanner创建一个新的扫描器对象,用于接受键盘数据Scannerscanner=newScanner(System.in);//使用next方式接收Stringsrt=scanner.next();//使用nextLine方式接收Stringsrt=scanner.nextLine();next():一定要读取到有效字符后才可以结束输入对输入有效字符之前......
  • 深入java虚拟机 - 垃圾收集 - 引用计数收集器
         引用计数是垃圾收集的早期策略。在这种方法中,堆中每一个对象都有一个引用计数。一个对象被创建了,并且指向该对象的引用被分配给一个变量,这个对象的引用计数被置为1。当任何其他变量被赋值为对这个对象的引用时,计数加1。当一个对象的引用超过了......
  • java 多线程 synchronized
    程序1:packagetestsynchronized;publicclassThread1implementsRunnable{ @Override publicvoidrun(){ synchronized(this){ for(inti=0;i<10;i++){ System.out.println(Thread.currentThread().getName() +"synchronizedloo......
  • java 一些奇怪的笔试题
    例1:packagecom.test;publicclassTest1{ publicvoidtestExit(){ try{ System.exit(0); }finally{ System.out.println("testExit"); } } publicvoidtestReturn(){ try{ return; }finally{ System.out.println("......
  • docker启动rabbitMQ,通过java创建交换机、队列、绑定
    通过docker启动rabbitMQdockerrun-d--namerabbitmq-p5671:5671-p5672:5672-p4369:4369-p25672:25672-p15671:15671-p15672:15672rabbitmq:management  将其修改为自启动:dockerupdaterabbitmq--restart=always  以下为通过java代码进行创建交换机......
  • 【Java设计模式】装饰者设计模式
    目录1.装饰者模式(Wrapper)概念2.装饰者是什么?3.装饰者设计模式-参与者3.1组成3.1Demo1.Component(抽象构件)2.ConcreteComponent(具体构件)3.Decorator(抽象装饰类)4.ConcreteDecorator(具体装饰类)5.Test类:3.2总结4.JDK源码中的装饰者设计模式扩展:开闭原则1.装饰者模式(Wra......
  • VScode 创建Java项目
    1.点击view找到命令行输入java(ctrl+shift+P)src:一般是自己写的源代码的文件lib:外部引用库等其他的内容bin:二进制程序,用以存放未来的编译文件等  ......
  • javascript操作cookie
    functionsetCookie(name,value){varDays=30;varexp=newDate();exp.setTime(exp.getTime()+Days*24*60*60*1000);document.cookie=name+"="+escape(value)+";expires="+exp.toGMTString();......
  • java 多线程的start()和run()的理解
    run()方法中是各个线程要执行的具体内容。所以当一个线程直接调用run()时那么直接开始执行方法体,这是在main线程中的多个线程只能时按照顺序的等待前面的线程结束run()方法的执行。而调用start方法只是线程进入准备阶段(Ready),并没有真正执行,这需要JVM进行分配时间片进行轮转线程执......
  • VScode配置Java环境
    1.安装VScode2.配置Java环境变量3.配置VScodeJava插件参考博文:VSCode配置调试编译java环境,史上最全!!!安装ExtensionPackforJava插件 VScode配置Java:home文件——首选项——设置输入javahome,选择在settings.json中编辑 {"window.zoomLevel":1,//"j......