Java变量自增是原子操作吗?volatile能保证原子操作吗?

先看一段简单的代码,用10个线程将一个int变量自增到10000。

package com.devnn.test;

import java.util.concurrent.CountDownLatch;

public class AddTest {
	
	public static int count;
	
	public static void main(String[] args) {
		
		System.out.println("start");
		
		CountDownLatch countDownLatch=new CountDownLatch(10);
		
		for (int i = 0; i < 10; i++) {
			
			new Thread() {
				
				public void run() {
					for(int j=0;j<1000;j++) {
						count++;
					}
					countDownLatch.countDown();
				};

			}.start();

		}
		try {
			countDownLatch.await();
			System.out.println("finished");
			System.out.println("count=" + count);
		} catch (InterruptedException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}
}

运行后,打印count的值是多少呢?

我们预期的结果是10000,实际上是不一定。

运行10次后,发现有几次结果是10000,有几次结果是小于10000。

为什么会出现这样的情况呢?

因为对变量自增操作并不是一个原子操作,变量自增其实分三步,一是读取变量,二是对变量加1,三是写回。非原子操作,在多线程操作势必导致线程不安全,即结果不稳定。

如果给count变量添加volatile修饰,能保证原子操作吗?

试一试,将上面代码count的声明修改成:

public static volatile int count;

其它代码不变。

运行结果依然跟之前一样。

这说明volatile并不能保证原子性。

那么如何才能将变量自增变成原子操作呢?

有两种方法,一种是使用AtomicInteger类型代替int类型,另一个是使用线程同步关键字synchronized。

下面使用第一种方法,使用AtomicInteger代替int类型。

package com.devnn.test;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

public class VolatileTest {
	
	public static AtomicInteger count=new AtomicInteger(0);
	
	public static void main(String[] args) {
		
		System.out.println("start");
		
		CountDownLatch countDownLatch=new CountDownLatch(10);
		
		for (int i = 0; i < 10; i++) {
			
			new Thread() {
				
				public void run() {
					for(int j=0;j<1000;j++) {
						count.getAndIncrement();
					}
					countDownLatch.countDown();
				};

			}.start();

		}
		try {
			countDownLatch.await();
			System.out.println("finished");
			System.out.println("count=" + count.get());
		} catch (InterruptedException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}
}

修改后,无论运行多少次,结果都是10000。

下面看第二种方法,使用synchronized同步关键字将自增操作变成原子操作。

package com.devnn.test;

import java.util.concurrent.CountDownLatch;

public class VolatileTest {
	
	public static int count=0;
	
	public static synchronized void add() {
		count++;
	}
	
	public static void main(String[] args) {
		
		System.out.println("start");
		
		CountDownLatch countDownLatch=new CountDownLatch(10);
		
		for (int i = 0; i < 10; i++) {
			
			new Thread() {
				
				public void run() {
					for(int j=0;j<1000;j++) {
						add();
					}
					countDownLatch.countDown();
				};
			}.start();
		}
		try {
			countDownLatch.await();
			System.out.println("finished");
			System.out.println("count=" + count);
		} catch (InterruptedException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}
}

使用synchronized修饰后,无论运行多少次,结果都是10000。

使用AtomicInteger比Synchronezed效率要高。AtomicInteger是java.util.concurrent.atomic包下的类,除此之外,还有其它原子操作类。详见
https://www.cnblogs.com/senlinyang/p/7856339.html

原创文章 56 获赞 44 访问量 9万+

猜你喜欢

转载自blog.csdn.net/devnn/article/details/99548790