欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

多线程编程-volatile关键字(三)

程序员文章站 2024-03-23 15:49:58
...

2.3 volatile关键字

Volatile关键字的主要作用是使变量的多个线程间可见。还有一点就是禁止指令重排序。

 

使用volatile关键字增加了实例变量在多个线程之间的可见性,但是volatile不能保证原子性。

关键synchronized和volatile的比较:

1)volatile是线程同步的轻量级实现,所以volatile性能比synchronized要好,并且volatile只能修饰变量,而synchronized可以修饰方法,代码块。

2)多线程访问volatile不会发生阻塞,而synchronized会出现阻塞。

3)Volatile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为他会将线程的私有内存和公共内存中的数据做同步。

4)Volatile解决的是变量在多个线程之间的可见性;synchronized解决的是多个线程之间资源访问的同步性。

2.3.1 volatile非原子特性

测试代码:

public class Thread1 extends Thread{
	public static int count =0;
	
	private static void addCount(){
		int index =0;
		while(index <100){
			count++;
			index++;
		}
		System.out.println("count = "+count);
	}
	@Override
	public void run(){
		addCount();
	}
}
public class RunDemo {

	public static void main(String[] args) {
		Thread1[] t1 = new Thread1[100];
		for(int i=0; i<100; i++){
			t1[i]= new Thread1();
		}
		for(int i=0; i<100; i++){
			t1[i].start();
		}		
	}
}

运行结果:

……

count = 9714

count = 9814

count = 9414

count = 9914

 

添加volatile关键字: public static int count=0;并不能解决这个问题,推荐的做法是使用synchronized关键字:synchronized privatestatic void addCount(){…},这种情况下volatile用不用都无所谓。

关键字volatile提示线程每次从共享内存读取变量,而不是从线程的工作内存读取,这保证了同步数据的可见性。但是这里的count++并不是一个原子操作,也就是非线程安全的。Count++可以分解为如下步骤:

1) 从内存取出count

2)计算count值(count  = count+1

3)将count值写入内存

在第二步时,如果其他线程也修改了count的值,就会出现脏读,解决这个问题的办法就是synchronized关键字。

下图展示了使用volatile时出现非线程安全的原因:

多线程编程-volatile关键字(三)

1) read和load阶段:从主存复制变量到线程工作内存;

2) use 和assign阶段;执行代码,改变共享变量值;

3) store 和write阶段:用线程工作内存数据刷新主存对应变量的值。

多线程环境下,use和assign是多次出现的,但这一操作不是原子性的,也即是在read和load后,如果主存count变量被修改了,线程工作内存中的值由于已经加载了,不会产生对应的变化,也就导致了工作内存和主存变量的不同步,出现了非线程安全。

Volatile修饰的变量,虚拟机只是保证从主存加载到线程工作内存的值是最新的,解决的是变量读时的可见性问题,无法保证操作的原子性,所以对于多线程访问同一个实例变量还要加锁同步。

2.3.2 使用原子类进行i++操作

除了使用synchronized实现同步外,还可以使用AtomicInteger原子类实现。

原子操作是不能分割的整体,没有其他线程能够中断或检查正在原子操作的变量。一个原子(atomic)类型就是一个原子操作可用的类型,他可以在没有锁的情况下做到线程安全。

public class Thread1 extends Thread{
	//volatile public static int count =0;
	private static AtomicInteger count = new AtomicInteger(0);
	//synchronized
	private static void addCount(){
		int index =0;
		while(index <100){
			//count++;
			count.incrementAndGet();
			index++;
		}
		System.out.println("count = "+count);
	}
	@Override
	public void run(){
		addCount();
	}
}
也可以得到期望结果。

相关标签: volatile关键字