- 首先,Java的原始数据类型(primitive datatypes),如short、int、double、long、boolean这些是非线程安全的;
- Java自带的线程安全的基本类型包括: AtomicInteger, AtomicLong, AtomicBoolean, AtomicIntegerArray,AtomicLongArray等
实验,验证int类型是非线程安全的:
300个线程,每个线程对共享变量 count 进行 5000 次 ++ 操作
测试代码:
public class PriThreadNotSafety extends Thread{
static int count = 0;
public void run(){
for (int k = 0; k < 5000; k++){
count++;
}
}
public static void main(String[] args) {
for (int i = 0;i < 300; i++){
new PriThreadNotSafety().start();
}
// 休眠10秒,以确保线程都已启动
try {
Thread.sleep(1000*10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(count);
}
}
- int 作为基本类型,直接存储在内存栈,且对其进行+,-操作以及++,–操作都不是原子操作,都有可能被其他线程抢断,所以不是线程安全
- int 用于单线程变量存取,开销小,速度快
期望输出150000,最后输出的是1493602
线程安全版
public class AtomicThreadSafety extends Thread {
private static AtomicInteger atomicInteger = new AtomicInteger(1);
static Integer count = Integer.valueOf(0);
@Override
public void run() {
for (int i = 0; i < 5000; i++) {
// getAndIncrement: 先获得值,再自增1,返回值为自增前的值
count = atomicInteger.getAndIncrement();
}
}
public static void main(String[] args) {
for (int k =0; k < 300; k++){
new AtomicThreadSafety().start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int excepted = 1500000;
int result = count;
Assert.assertEquals(excepted,result); // True
}
返回结果1500000
AtomicInteger类中有有一个变量valueOffset,用来描述AtomicInteger类中value的内存位置 。
当需要变量的值改变的时候,先通过get()得到valueOffset位置的值,也即当前value的值.给该值进行增加,并赋给next
compareAndSet()比较之前取到的value的值当前有没有改变,若没有改变的话,就将next的值赋给value,倘若和之前的值相比的话发生变化的话,则重新一次循环,直到存取成功,通过这样的方式能够保证该变量是线程安全的
- value使用了volatile关键字,使得多个线程可以共享变量,使用volatile将使得VM优化失去作用,在线程数特别大时,效率会较低。