volatile属性:可见性、保证有序性、不保证原子性。
Java的内存中所有的对象都存在主内存中,每个线程都有自己的栈和程序计数器,多个线程对同一个对象的变量读取时,会将对象的变量从主内存中拷贝到自己的栈帧里(操作数栈),线程之间也无法直接访问对方的操作数栈,只能通过主内存传递变量的值;
可见性:如果对声明了volatile变量进行写操作时,JVM会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写会到系统内存。 这一步确保了如果有其他线程对声明了volatile变量进行修改,则立即更新主内存中数据。
有序性:但这时候其他处理器的缓存还是旧的,所以在多处理器环境下,为了保证各个处理器缓存一致,每个处理会通过嗅探在总线上传播的数据来检查 自己的缓存是否过期,当处理器发现自己缓存行对应的内存地址被修改了,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作时,会强制重新从系统内存把数据读到处理器缓存里。 这一步确保了其他线程获得的声明了volatile变量都是从主内存中获取最新的。
使用场景:状态标记,后续会再写另一个使用场景,单例模式下的volatile确保单例对象的返回的正确性。
原子操作:可以是一个步骤,也可以是多个步骤,但是其顺序不可以被打断,也不可以被切面只执行其中的一部分(不可中断性)。
并发示例:
public class Counter {
volatile int i=0;
public void add(){
//非原子操作 可以加锁synchronized或者ReentrantLock等保证原子性
i++;
}
}
public class Main {
public static void main(String[] args) throws Exception {
final Counter counter=new Counter();
for(int i=0;i<5;i++){
new Thread(new Runnable(){
@Override
public void run() {
for(int j=0;j<10000;j++){
counter.add();
}
System.out.println("Done......");
}
}).start();
}
//主线程进行等待
Thread.sleep(5000);
System.out.println(counter.i);
}
}
其中上述add()方法对应的汇编指令,其中i++被编译成下面四条指令,因此无法实现原子性:
public void add();
flags: ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: dup
2: getfield #2 // Field i:I 【从主内存中加载到操作数栈】
5: iconst_1 【将1赋值给变量】
6: iadd
7: putfield #2 // Field i:I 【将计算结果放回到主内存】
10: return
LineNumberTable:
line 11: 0
line 12: 10
CAS(Compare and swap)
Compare and swap 比较和交换,属于硬件同步原语,处理器提供了基本内存操作的原子性保证。
CAS操作需要输入两个数值,一个旧值A和一个新值B,在操作期间对旧值进行比较,若没有发生变化,才交换成新值,发生了变化则不交换。
Java中的sun.misc.Unsafe类,提供了compareAndSwapInt()和compareAndSwapLong()等几个方法实现CAS。
JDK中很多工具类底层都是用到了CAS机制:
原子操作类:AtomicInteger、AtomicIntegerArray、AutomicIntegerFieldUpdater
并发操作类:ReentrantLock、ReentrantReadWriteLock、AQS、Semphore、CountDownLatch
Map:ConcurrentHashMap、ConcurrentSkipListMap
List:CopyOnWriteArrayList
Set:CopyOnWriteArraySet、ConcurrentSkipListSet
线程池:ThreadPoolExecutor
使用CAS实现锁:
public class CounterUnsafe
{
volatile int i=0;
private static Unsafe unsafe=null;
private static long valueOffset;
static{
try {
//反射出unsafe对象
Field field=Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe=(Unsafe)field.get(null);
//获取Counter类中的字段i的偏移量
Field ifield=Counter.class.getDeclaredField("i");
valueOffset=unsafe.objectFieldOffset(ifield);
}catch (NoSuchFieldException |IllegalAccessException e){
e.printStackTrace();
}
}
public void add(){
for(;;){
//拿到旧的值
int current=unsafe.getIntVolatile(this,valueOffset);
//进行+1操作
int newValue=current+1;
//如果写会到堆内存成功
if(unsafe.compareAndSwapInt(this,valueOffset,current,newValue)){
break;
}
}
}
}
Counter类进行加锁实现:
//对变量进行加锁实现
public class Counter {
volatile int i=0;
Lock lock=new MyReentrantLock(); //自定义锁
public void add(){
lock.lock();
i++;
lock.unlock();
}
}
//自定义锁
public class MyReentrantLock implements Lock{
//标记锁的持有线程
AtomicReference<Thread> owner=new AtomicReference<>();
//等待的线程队列 BlockQueue [ 如果当前没有可用的空间 add 抛异常,offer 返回false,put阻塞][remove,poll,take]
private LinkedBlockingQueue<Thread> waiters=new LinkedBlockingQueue<>();
@Override
public void lock(){
//如果未获取到锁
if(!tryLock()){
waiters.offer(Thread.currentThread());
//1、线程挂起的几种方式 [condition] [Suspend.resume() 容易造成死锁,已经被弃用] [wait nofity 必须与synchronized 关键字一起使用]
for(;;){
//判断是否是线程的头部,如果不是继续循环
Thread peek = waiters.peek(); //peek拿到元素,元素本身不出队列
if(peek!=Thread.currentThread()){
continue;
}
//如果是线程的头部则尝试着去拿锁
if(tryLock()){
waiters.poll();// poll拿到元素,元素出队列
return;
}
//没有拿到锁,则将线程挂起
LockSupport.park();
}
}
}
@Override
public boolean tryLock() {
return owner.compareAndSet(null,Thread.currentThread());
}
@Override
public void unlock() {
if(tryUnlock()){
Thread thread=waiters.peek();
if(thread!=null){
LockSupport.unpark(thread); //唤醒线程
}
}
}
public boolean tryUnlock(){
if(owner.get()!=Thread.currentThread()){
throw new IllegalMonitorStateException();
}else{
return owner.compareAndSet(Thread.currentThread(),null);
}
}
@Override
public void lockInterruptibly() throws InterruptedException{
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public Condition newCondition() {
return null;
}
}
其中Unsafe参见:Java魔法类:Unsafe应用解析