原子性:在java语言原子性是指的一个不可以分割的操作,比如说 a = 0,这个就具有原子性,如果是a++, 这个操作其实是 a = a+1; 是可以分割的,所以他就不是一个原子性操作。
非原子操作具有线程安全问题,我们需要使用相关的手段,保证线程同步,java.util.concurrent.atomic包下面提供了一系列的原子操作类,如AtomicInteger、AtomicLong、AtomicReference等
可见性:是指线程之间的可见性,当一个线程修改一个变量,另外一个线程可以马上得到这个修改值。比如:用volatile修饰的变量是具有可见性的。volatile修饰的变量不允许线程内部缓存和
重新排序,即直接修改内存,所以对其它线程是可见的。但是这里需要注意一个问题volatile修饰的变量只是具有可见性,而不具备原子性。比如 volatile int a = 0; 之后操作一个 a++,那么a只是具有可见性,而
不具备原子性,a++同样具有线程安全的问题。
他们之间关系
原子性是一个操作是否一步完成,可见性是指其它线程是否可见。它们其实并没有任何关系。
public class Test {
volatile int a = 1;
volatile boolean ready;
public class PrintA extends Thread{
@Override
public void run() {
while(!ready){
Thread.yield();
}
System.out.println(a);
}
}
public static void main(String[] args) throws InterruptedException {
Test t = new Test();
t.new PrintA().start();
//下面两行如果不加volatile的话,执行的先后顺序是不可预测的。并且下面两行都是原子操作,但是这两行作为一个整体的话就不是一个原子操作。
t.a = 48; //这是一个原子操作,但是其结果不一定具有可见性。加上volatile后就具备了可见性。
t.ready = true;//同理
}
}
上面程序如果变量a不用volatile修饰那么输出结果很可能就是0.。
AtomicInteger、AtomicLong、AtomicBoolean,AtomicReference前三个都很好理解是对Integer,Long,Boolean做原子性操作的类,那这个AtomicReference是什么呢?
它主要是对引用对象进行原子性操作:举个栗子。
class Person {
volatile long id;
public Person(long id) {
this.id = id;
}
public String toString() {
return "id:"+id;
}
public static void main(String[] args){
// 创建两个Person对象,它们的id分别是101和102。
Person p1 = new Person(101);
Person p2 = new Person(102);
// 新建AtomicReference对象,初始化它的值为p1对象
AtomicReference ar = new AtomicReference(p1);
// 通过CAS设置ar。如果ar的值为p1的话,则将其设置为p2。
ar.compareAndSet(p1, p2);
Person p3 = (Person)ar.get();
System.out.println("p3 is "+p3);
System.out.println("p3.equals(p1)="+p3.equals(p1));
}
运行结果:
p3 is id:102
p3.equals(p1)=false
结果说明:
新建AtomicReference对象ar时,将它初始化为p1。
紧接着,通过CAS函数对它进行设置。如果ar的值为p1的话,则将其设置为p2。
最后,获取ar对应的对象,并打印结果。p3.equals(p1)的结果为false,这是因为Person并没有覆盖equals()方法,而是采用继承自Object.java的equals()方法;而Object.java中的equals()实际上是调用"=="去比较两个对象,即比较两个对象的地址是否相等。
网友评论