先看一段简单的代码,用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