一:原子操作
原子(atom)本意是“不能被进一步分割的最小粒子”,而原子操作(atomic operation)意为“不可被中断的一个或一系列操作”
原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch
二:CAS
CAS:Compare and Swap, 翻译成比较并交换。
java.util.concurrent包中借助CAS实现了区别于synchronized同步锁的一种乐观锁。
CAS通过调用JNI的代码实现的。JNI:Java Native Interface为JAVA本地调用,允许java调用其他语言。
而compareAndSwapInt就是借助C来调用CPU底层指令实现的。
CAS并不是无阻塞,只是阻塞并非在语言、线程方面,而是在硬件层面,所以无疑这样的操作会更快更高效!
下面是sun.misc.Unsafe类的compareAndSwapInt()方法的源代码:
public final native boolean compareAndSwapInt(Object o, long offset,
int expected,
int x);
三:Volatile(最好先了解下JMM内存模型)
Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。
当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。
举个列子:以双CPU为例a、bCPU,现在有A线程 B线程,共享(全局)变量i 其中A线程在aCPU执行,B线程在bCPU执行,如果变量i使用了volatile那么不管是A 线程还是B线程将I的值变化后立马会写到主内存当中,如果不用volatile,那么i的值会存在cpu的笨蛋内存中,并不会马上写入我们的主内存。
具体什么时候刷新共享数据到主内存是不确定的
四:java原子操作
java的并发包java.util.concurrent.atomic.* 提供了原子操作的相关类,其中类可以分成4组
标量类:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
数组类:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
更新器类:AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater
复合变量类:AtomicMarkableReference,AtomicStampedReference
1:标量类 AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
这四种基本类型用来处理布尔,整数,长整数,对象四种数据。
构造函数(两个构造函数)
默认的构造函数:初始化的数据分别是false,0,0,null
带参构造函数:参数为初始化的数据
set( )和get( )方法:可以原子地设定和获取atomic的数据。类似于volatile,保证数据会在主存中设置或读取
getAndSet( )方法
原子的将变量设定为新数据,同时返回先前的旧数据
其本质是get( )操作,然后做set( )操作。
尽管这2个操作都是atomic,但是他们合并在一起的时候,就不是atomic。在Java的源程序的级别上,如果不依赖synchronized的机制来完成这个工作,是不可能的。只有依靠native方法才可以。
compareAndSet( ) 和weakCompareAndSet( )方法
这两个方法都是conditional modifier方法。这2个方法接受2个参数,一个是期望数据(expected),一个是新数据(new);如果atomic里面的数据和期望数据一致,则将新数据设定给atomic的数据,返回true,表明成功;否则就不设定,并返回false。
对于AtomicInteger、AtomicLong还提供了一些特别的方法。getAndIncrement( )、incrementAndGet( )、getAndDecrement( )、decrementAndGet ( )、addAndGet( )、getAndAdd( )以实现一些加法,减法原子操作。(注意 --i、++i不是原子操作,其中包含有3个操作步骤:第一步,读取i;第二步,加1或减1;第三步:写回内存)
代码例子
public class AtomicIntegerTest extends Thread {
public static AtomicInteger astom_i = new AtomicInteger(100);
public void run() {
//System.out.println(Thread.currentThread().getName() + ":incrementAndGet:" + (astom_i.incrementAndGet()));
System.out.println(Thread.currentThread().getName() + ":incrementAndGet:" + (astom_i.getAndIncrement()));
// System.out.println(Thread.currentThread().getName() + ":getAndDecrement:" + (astom_i.getAndDecrement()));
// System.out.println(Thread.currentThread().getName() + ":getAndDecrement:" + (astom_i.decrementAndGet()));
}
public static void main(String[] args) {
AtomicIntegerTest counter = new AtomicIntegerTest();
Thread t1 = new Thread(counter);
Thread t2 = new Thread(counter);
Thread t3 = new Thread(counter);
Thread t4 = new Thread(counter);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
public class AtomicReferenceTest {
public static AtomicReference atomicUserRef = new AtomicReference();
public static void main(String[] args) {
User user = new User("conan", 15);
atomicUserRef.set(user);
User updateUser = new User("Shinichi", 17);
atomicUserRef.compareAndSet(user, updateUser);
System.out.println(atomicUserRef.get().getName());
System.out.println(atomicUserRef.get().getOld());
}
static class User {
private String name;
private int old;
public User(String name, int old) {
this.name = name;
this.old = old;
}
public String getName() {
return name;
}
public int getOld() {
return old;
}
}
}
2数组类:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
分别表示整数数组、long型数组和普通的对象数组
下面给出一个简单的示例,展示AtomicIntegerArray使用:
public class AtomicIntegerArrayDemo {
static AtomicIntegerArray arr = new AtomicIntegerArray(10);
public static void main(String[] args) throws InterruptedException {
Thread[] ts = new Thread[10];
for (int k = 0; k < 10; k++) {
ts[k] = new Thread(new AddThread());
}
for (int k = 0; k < 10; k++) {
ts[k].start();
}
for (int k = 0; k < 10; k++) {
ts[k].join();
}
System.out.println(arr);
}
}
class AddThread implements Runnable {
public void run() {
for (int k = 0; k < 100; k++)
AtomicIntegerArrayDemo.arr.getAndIncrement(k % AtomicIntegerArrayDemo.arr.length());
}
}
运行结果:[100, 100, 100, 100, 100, 100, 100, 100, 100, 100]
上述代码第2行,申明了一个内含10个元素的数组。第3行定义的线程对数组内10个元素进行累加操作,每个元素各加100次。第11行,开启10个这样的线程。因此,可以预测,如果线程安全,数组内10个元素的值必然都是100。反之,如果线程不安全,则部分或者全部数值会小于100。
这说明AtomicIntegerArray确实合理地保证了数组的线程安全性。
3: 更新器类 AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater
基于于反射的实用工具,可以对指定类的指定 volatile 字段进行原子更新。API非常简单,但是也是有一些约束:
(1)字段必须是volatile类型的
(2)字段的描述类型(修饰符public/protected/default/private)是与调用者与操作对象字段的关系一致。也就是说 调用者能够直接操作对象字段,那么就可以反射进行原子操作。但是对于父类的字段,子类是不能直接操作的,尽管子类可以访问父类的字段。
(3)只能是实例变量,不能是类变量,也就是说不能加static关键字。
(4)只能是可修改变量,不能使final变量,因为final的语义就是不可修改。实际上final的语义和volatile是有冲突的,这两个关键字不能同时存在。
(5)对于AtomicIntegerFieldUpdater 和AtomicLongFieldUpdater 只能修改int/long类型的字段,不能修改其包装类型(Integer/Long)。如果要修改包装类型就需要使用AtomicReferenceFieldUpdater
代码:
public class AtomicIntegerFieldUpdaterTest {
private static AtomicIntegerFieldUpdater a = AtomicIntegerFieldUpdater
.newUpdater(User.class, "old");
public static void main(String[] args) {
User conan = new User("conan", 10);
System.out.println(a.getAndIncrement(conan));
System.out.println(a.get(conan));
}
public static class User {
private String name;
public volatile int old;
public User(String name, int old) {
this.name = name;
this.old = old;
}
public String getName() {
return name;
}
public int getOld() {
return old;
}
}
}