前言
在使用volatile关键字后,并不会完全解决线程安全问题,它只是解决线程可见性问题。
jdk1.5后推出了并发包:java.util.concurrent.atomic可以解决线程原子性的问题。
常用方法
- get 具有读取 volatile 变量的内存效果。
- set 具有写入(分配)volatile 变量的内存效果。
- weakCompareAndSet 以原子方式读取和有条件地写入变量但不 创建任何 happen-before 排序,因此不提供与除 weakCompareAndSet 目标外任何变量以前或后续读取或写入操作有关的任何保证。
- compareAndSet 和所有其他的读取和更新操作(如 getAndIncrement)都有读取和写入 volatile 变量的内存效果。
volatile出现的问题
public class ThreadAtomic{
public static void main(String[] args) {
Ticket ticket = new Ticket();
for (int i = 0;i<1000;i++) {
new Thread(ticket).start();
}
}
}
class Ticket implements Runnable{
public volatile int count = 0;
@Override
public void run() {
count++;
System.out.println("当前线程:"+Thread.currentThread().getName()+":"+count);
}
}
依然存在数据问题
打印如下:
当前线程:Thread-933:920
当前线程:Thread-913:915
当前线程:Thread-912:914
当前线程:Thread-925:913
当前线程:Thread-924:912
i++的原子性问题:i++ 的操作实际分了三个步骤“读-改-写”
atomic特性
- 里面的变量全部用volatile关键值修饰,保证内存可见性
- CAS(Compare-And-Swap)算法保证数据的原子性(CAS算法是硬件对于并发操作共享数据的支持,jvm底层大量的支持)
CAS包含三个操作数:内存值 V,预估值 A,更新值 B 。如果 V == A 时,V = B 。否则,不做任何操作。
Atomic类解决原子性
在java.util.concurrent.atomic可以解决线程原子性的问题 , 类如下:
AtomicBoolean
AtomicInteger
AtomicIntegerArray
AtomicIntegerFieldUpdater
AtomicLong
AtomicLongArray
AtomicLongFieldUpdater
AtomicMarkableReference
AtomicReference
AtomicReferenceArray
AtomicReferenceFieldUpdater
AtomicStampedReference
DoubleAccumulator
DoubleAdder
LongAccumulator
LongAdder
Striped64
使用AtomicInteger
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadAtomic{
public static void main(String[] args) {
Ticket ticket = new Ticket();
for (int i = 0;i<1000;i++) {
new Thread(ticket).start();
}
}
}
class Ticket implements Runnable{
// public volatile int count = 0;
public AtomicInteger count = new AtomicInteger(0);
@Override
public void run() {
// System.out.println("当前线程:"+Thread.currentThread().getName()+":"+count++);
// 相当于count++
System.out.println("当前线程:"+Thread.currentThread().getName()+":"+count.getAndIncrement());
}
}
打印如下:
当前线程:Thread-992:992
当前线程:Thread-994:993
当前线程:Thread-993:994
当前线程:Thread-995:995
当前线程:Thread-996:996
当前线程:Thread-998:997
当前线程:Thread-999:998
当前线程:Thread-997:999
模拟CAS算法
/**
* 模拟CAS算法
* @author terry
* @date 2018年5月26日
*/
public class TestCompareAndSwap {
public static void main(String[] args) {
final CompareAndSwap cas = new CompareAndSwap();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
int expectedValue = cas.get();
boolean b = cas.compareAndSet(expectedValue, (int)(Math.random() * 101));
System.out.println(b);
}
}).start();
}
}
}
class CompareAndSwap{
private volatile int value;
public synchronized int get(){
return value;
}
public synchronized int compareAndSwap(int expectedValue,int newValue){
int oldValue = value;
if (expectedValue == oldValue) {
this.value = newValue;
}
return oldValue;
}
public synchronized boolean compareAndSet(int expectedValue,int newValue){
return expectedValue == compareAndSwap(expectedValue,newValue);
}
}