在jdk中提供了一些java.util.concurrent.atomic原子操作类。对于原子类变量的操作是不会存在并发性问题的,不需要使用synchronized关键字进行并发控制。它底层自身的实现即可保证变量的可见性以及操作的原子性,一般我们可以使用AtomicInteger,AtomicLong等实现计数器等功能,利用AtomicBoolean实现标志位等功能。
public class Message {
// 实现一个id自增计数器
private static AtomicLong count = new AtomicLong();
//无需进行锁并发控制,防止id重复
public Long getMsgId(){
return count.getAndIncrement();
}
}
一般实现线程安全的常见两种机制:
原子量底层的实现均是采用CAS非阻塞算法实现的,是无锁(lock-free)算法中最有名的一种(无锁算法:不使用锁机制来实现线程安全的算法,采用锁机制都会存在线程为请求锁而产生阻塞的情况),CAS不会阻塞线程从而不会带来CPU上下文切换的性能开销。
CAS的全称是Compare-And-Swap(意思是比较后交换):指当两者(这个两者是指线程栈内存中备份的变量值和主内存中共享变量值)进行比较时,如果值相等,则证明共享数据没有被其他线程修改过,则替换成新值,然后继续往下运行;如果不相等,说明主内存中的共享数据被其它线程修改过,放弃已经所做的操作,然后重新执行刚才的操作(可见CAS算法的关键就是这个循环体结构,退出循环的条件是主内存中的共享数据没有被其他线程修改过,如果被修改过,则该线程会重复执行此操作,直到满足退出循环体的条件为止,这也是为什么线程不会阻塞的原因)。容易看出 CAS 操作是基于共享数据不会被修改的假设,采用了类似于数据库的 commit-retry 的模式。当同步冲突出现的机会很少时,这种假设能带来较大的性能提升。
下面以jdk AtomicInteger类的具体实现为例,进行说明:
public class AtomicInteger extends Number implements java.io.Serializable {
private static final longserialVersionUID = 6214790243416807050L;
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private volatile int value;
//value字段相对AtomicInteger对象内存地址的偏移量
private static final longvalueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { thrownew Error(ex); }
}
//该AtomicInteger原子量对应的值value(共享变量)
private volatile int value;
//返回当前value
public final int get() {
return value;
}
//该方法的作用value++的线程安全版,返回自增长以前的值
public final int getAndIncrement() {
//CAS算法中循环体结构
for (;;) {
int current = get();
int next = current + 1;
//主内存变量更新成功,则退出循环体,否则重复执行此自增操作,直到更新成功为止
if (compareAndSet(current, next))
return current;//返回自增长以前的值
}
}
//拿expect和主内存中的值进行比较,如果相等,说明主内存中的值没有发生过修改
//则将新值写入到主内存,return true。否则直接return false;
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
}
参考链接: