3、使用Atomicxxxx保证原子性:
public class Test {
static AtomicInteger a = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
for(int i=0; i<10000; i++) {
new Thread(()-> {
a.getAndAdd(1);
}).start();
new Thread(()-> {
a.getAndAdd(1);
}).start();
}
Thread.sleep(1000); //为了保证不会影响,停一秒再写
System.out.println(a); //对a执行读操作
}
}
可以看到,a++的操作改成了getAndAdd(),读和写是一起执行的,这就不会在读值之后写值之前被其他线程插一杠子。
需要注意的一点是,原子性和可见性并不是相互独立的,保证原子性的前提是保证可见性,那为什么我们没有再用volatile修饰a来保证可见性呢?这就需要去看看AtomicInteger的源码了:
其实它的内部也使用了volatile关键字。
4、使用synchronized同步代码段强制实现原子性和可见性
除了Atomic,也可以使用synchronized同步代码段强制实现原子性。
public class Test {
static AtomicInteger a = new AtomicInteger(0);
static int b = 0;
public static void main(String[] args) throws InterruptedException {
for(int i=0; i<10000; i++) {
new Thread(()-> {
// a.getAndAdd(1);
synchronized(Test.class) {
b++;
}
}).start();
new Thread(()-> {
// a.getAndAdd(1);
synchronized(Test.class) {
b++;
}
}).start();
}
Thread.sleep(1000); //为了保证不会影响,停一秒再写
// System.out.println(a); //对a执行读操作
System.out.println(b); //对b执行读操作
}
}
相比较Atomic,synchronized就更加重量级了。
另外:volatile不具有传染性,用volatile修饰的对象的内部属性不具有可见性,反之用volatile修饰的内部属性也不能保证所在对象的可见性。