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应用解析
