java高并发、多线程(四)
AtomicInteger
在上一节中我们举了下面这样一个例子:
public class SynchronizedTest {
static int count = 0;
public synchronized static void add() {
for (int i = 0; i < 10000; i++) {
count++;
}
}
public static void main(String[] args) {
List<Thread> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(new Thread(SynchronizedTest::add,"Thread-"+i));
}
list.forEach(t->t.start());
list.forEach(t-> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(count);
}
}
如果我们不使用synchronized可以达到相同目的吗?
答案是肯定的,可以使用AtomicInteger类来完成相应功能。
将上述代码修改为:
static AtomicInteger count = new AtomicInteger(0);
public static void add() {
for (int i = 0; i < 10000; i++) {
//等同于count++
count.incrementAndGet();
}
}
运行结果:
为什么不使用synchronized,上述程序仍然能达到线程安全呢?
答案很明显在AtomicInteger类上,因为它是线程安全的,它能保证它的操作是原子性的。那它是如何保证的呢?我们来查看它的源码:
从上面可以看到,该类使用了unsafe类及其中的compareAndSwapInt方法。
- unsafe类
从名字上可以看出这个类是不安全的,因为它给了java像C/C++一样操作内存空间的能力。例如:
public class UnsafeTest {
public static void main(String[] args) {
// 通过getUnsafe()方法获取unsafe对象将抛出SecurityException异常
// 但是我们发现unsafe类源码中有private static final Unsafe theUnsafe属性;因此在这里我们可以通过反射获取unsafe对象
Field theUnsafe = null;
Unsafe unsafe = null;
try {
theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
unsafe = (Unsafe)theUnsafe.get(null);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// 创建num一个实例对象
NUM num = new NUM();
// 通过反射获取num对象的a属性
Field a = null;
try {
a = num.getClass().getDeclaredField("a");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
// 通过unsafe获取a的值
int anInt = unsafe.getInt(num, unsafe.objectFieldOffset(a));
System.out.println(anInt);
// 通过unsafe修改a的值
unsafe.putInt(num,unsafe.objectFieldOffset(a),10);
System.out.println(num.getA());
}
}
/**
* 创建一个类,具有私有属性a
*/
class NUM{
private int a;
public NUM() {
this.a = 1;
}
public int getA() {
return a;
}
}
运行结果:
- compareAndSwapXXX()方法
CAS(Compare And Swap)是一种无锁的比较交换策略,我们通常使用的synchronized 是一种悲观锁,它所假定的是线程在运行中总会遇上冲突,因此首先就给同步代码块上锁,致使性能降低。但是CAS不用去加锁,它所使用的方式是通过比较属性值与期望值相同,那么就直接修改,不同则循环重试。在性能上要优于synchronized。在java的并发包中大多数类都使用的是该策略。