java中的volidate用法及注意事项

                               java中的volatile用法及注意事项

在应用多线程的时候,我们经常把volitile关键字和synchronized关键字比较。

synchronized是多线程中的锁机制,利用synchronized关键字,同一时间只允许一个线程访问共享资源(读写)

volidate也经常用于多线程中,注意volitile并不能保证数据更新同步,一个变量声明为volitile,

只能保证数据的可见性,一个线程在修改变量的时候,另一个线程能准确读到变量。

但是不能保证原子性操作。多个线程同时写这个变量的时候会出错。

在一定情况下使用volitile能提高效率,因为使用volitile关键字声明后,访问这个变量不需要切换线程,

比如多个线程中只有一个线程修改变量,其他线程读这个读变量,这时候用volitile声明关键字会提高效率。

一般volitile不适合的场景:

1、不能做线程安全计数器

2、不能用来修饰多个线程可修改的变量

为了确保共享变量能被准确和一致地更新 ,线程应该确保通过排他锁(synchronized)单独的获取这个变量或者通过CAS

原子操作

简述下线程的排他锁过程:

1、线程通过调用cpu修改值之后,会将缓存行中的数据写回到内存中去。在执行写回操作时,处理器可以独占任何

共享空间,而且最新的处理器,lock#信号不会锁总线,而是锁缓存,锁住总线的话,占用的资源太大。

2、写回操作会使在其他cpu里缓存了该内存地址的数据无效。例如在Pentium和P6 faminly处理器中,如果通过嗅探

一个处理器来检测其他处理器打算写的内存地址,而这个地址处于共享状态,那么正在嗅探的处理器将会使其自身的

缓存行无效,下次访问相同的内存地址时,强制执行缓存行填充。

这一部分也是由JMM(java内存模型控制的)。

java中的volidate用法及注意事项_第1张图片


此部分参考博客: volatile底层实现原理和其应用

当一个声明为volatile变量时,读写是可以控制线程对共享变量的读写互斥,即get,set方法操作共享变量是互斥的。

满足原子性的特点。但是volidate变量的++操作不满足原子性:

VolatileNode.java:

package LaboratoryReport;

public class VolatileNode {

	int vl;
	public VolatileNode(int i) {
		this.vl=i;
		// TODO Auto-generated constructor stub
	}
	public synchronized void set(int vl) {
		this.vl=vl;
	}
	public synchronized int get() {
		return this.vl;
	}
	public void add() {
		int temp=get();
		temp+=1l;
		set(temp);
	}
}

TestVolatile.java:

package LaboratoryReport;


public class TestVolatile extends Thread{
	private VolatileNode a;
	private String name;
	public TestVolatile(VolatileNode a2,String s) {
		this.a=a2;
		this.name=s;
	}
	public void run() {
		if(this.name=="线程A") {
			for(int i=0;i<10000;i++) {
				this.a.set(i);
			}
		}
		else
		{
			for(int i=10000;i<20000;i++)
				this.a.set(i);
		}
	}
	public static void main(String[] args) throws InterruptedException {
		VolatileNode a=new VolatileNode(1);
		TestVolatile aTestThread=new TestVolatile(a,"线程A");
		TestVolatile bTestThread=new TestVolatile(a,"线程B");
		aTestThread.start();
		bTestThread.start(); 
		Thread.sleep(1000);
		System.out.println(a.get());
	}
}

java中的volidate用法及注意事项_第2张图片

package LaboratoryReport;


public class TestVolatile extends Thread{
	private VolatileNode a;
	private String name;
	public TestVolatile(VolatileNode a2,String s) {
		this.a=a2;
		this.name=s;
	}
	public void run() {
		for(int i=0;i<10000;i++) {
			this.a.add();
		}
	}
	public static void main(String[] args) throws InterruptedException {
		VolatileNode a=new VolatileNode(1);
		TestVolatile aTestThread=new TestVolatile(a,"============");
		TestVolatile bTestThread=new TestVolatile(a,"yyyyyyyyyyyy");
		aTestThread.start();
		bTestThread.start(); 
		Thread.sleep(1000);
		System.out.println(a.get());
	}
}

java中的volidate用法及注意事项_第3张图片

最后这个共享变量没有加到20000,所以用volitile共享变量的时候注意共享变量的使用,因为涉及到多个线程++的时候

是不安全的,因为++并不直接改变共享变量的值,而需要更多的方式达到++操作。有兴趣的小伙伴可以把上面实现的++

操作直接换成++,也是不能保证线程同步的。(笔者亲试)

解决策略:

1:用类实现runnable接口代替继承thread。

2:用线程锁synchronized来实现线程同步

3:线程启动用run方法。

你可能感兴趣的:(Java)