zoukankan      html  css  js  c++  java
  • CAS在Java类中的应用

    CAS

    这个指令全称 compare and swap 即比较替换指令,在现代处理器新加入的指令。
    指导思想:基于乐观锁机制。比较一个变量在内存值中的值和变量的当前值(旧值)。如果相等,则认为该变量没有发生改变,使用新值替代旧值;否则认为替换失败。

    Unsafe

    在java程序的多线程环境中,如果一个变量被多个线程访问,要保证线程安全,除了 volatile、锁、final、static这些手段外,可以借助java提供的 sun.misc.Unsafe类
    这个类两个特点:
    1.平台相关的(java语言平台无关),所有方法为native类型,c语言写的。提供了很多native类型方法
    更详细点的源码可以看-->OpenJDK中的Unsafe
    2.我们可以以不安全的方式使用这个类,在下面的代码中getUnsafe()给出了推荐的使用方式。使用这个类时也可以借鉴一些JDK中自带的类,比如并发包中的原子类、Random、LockSupport.park()、ConcurrentHashMap等。

    下面贴出我对Unsafe类源码的翻译分析

    package sun.misc;
    
    import java.security.*;
    import java.lang.reflect.*;
    
    import sun.reflect.CallerSensitive;
    import sun.reflect.Reflection;
    
    
    /**
     * 一个执行底层非安全的方法集合。虽然这个类及所有方法是公共的,
     * 使用这个类是受限制的,只有被信任的代码才能获得这个类的实例。
     * @author John R. Rose
     * @查阅 方法getUnsafe()
     */
    
    public final class Unsafe {
    
        private static native void registerNatives();
        static {
            registerNatives();
            sun.reflect.Reflection.registerMethodsToFilter(Unsafe.class, "getUnsafe");
        }
    	// 私有构造方法,用于单例
        private Unsafe() {}
    	// 私有静态常量属性
        private static final Unsafe theUnsafe = new Unsafe();
    
        /**
         * 这个类的多数方法是底层的,对应于一些硬件指令(在特定的机器)。
    	 * 编译器会积极优化这个方法。
    	 * 对于使用不安全的操作,一个比较推荐的语法:
         * class MyTrustedClass {
         *   private static final Unsafe unsafe = Unsafe.getUnsafe();
         *   ...
         *   private long myCountAddress = ...;
         *   public int getCount() { return unsafe.getByte(myCountAddress); }
         * }
         */
        @CallerSensitive
        public static Unsafe getUnsafe() {
            Class<?> caller = Reflection.getCallerClass();
            if (!VM.isSystemDomainLoader(caller.getClassLoader()))
                throw new SecurityException("Unsafe");
            return theUnsafe;
        }
    
        /* 
    		这个只作用在Java堆的对象属性上。
    		不作用在数组元素上。*/
        public native int getInt(Object o, long offset);
    
        /**
         * 将值存入Java变量。
    	 * 前两个参数和getInt()方法一样,给定的值x被存入变量。
         * 变量必须和方法的参数x同样的类型。
         * @param o 参数o是java堆对象,即任何变量或null所寄存的地方。
         * @param offset 表示变量寄存在Java堆的哪个位置,可以用一个内存地址定位变量位置。
    				注意:此处用long类型表示内存地址,正是因为long 8 个字节,支持64位处理器。
    				如果用int表示内存地址,那只能支持32处理器。
         * @param x 被存入Java变量中的值
         * @throws RuntimeException No defined exceptions are thrown, not even
         *         {@link NullPointerException}
         */
        public native void putInt(Object o, long offset, int x);
    
    
        /**
    	 * 这个方法,像所有其他32位偏移,在之前的发行版本1.4中是native方法,这里为了向后兼容1.4
         * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long.
         * See {@link #staticFieldOffset}.
         */
        @Deprecated
        public int getInt(Object o, int offset) {
            return getInt(o, (long)offset);
        }
    
        /**
         * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long.
         * See {@link #staticFieldOffset}.
         */
        @Deprecated
        public void putInt(Object o, int offset, int x) {
            putInt(o, (long)offset, x);
        }
    
    
        /**
         * 从内存地址获取原生的指针,如果地址是0,或者没有指向一个内存地址块,结果是未知的。
         * 如果原生指针小于64位宽度,它会被作为无符号数扩展到64位。
    	 * 这个指针可以被给定的字节偏定位。从目标地址读到的字节数量可能由地址宽度决定。
         * @see #allocateMemory
         */
        public native long getAddress(long address);
    
        /**
         * 存储一个原生指针到给定的内存地址。如果地址是0或者超过范围,结果将是未知的。
         * 写入到目标地址的实际字节数量是用地址宽度决定的。
         * @see #getAddress(long)
         */
        public native void putAddress(long address, long x);
    
        /// wrappers for malloc分配内存, realloc扩大内存, free释放内存:
    		
        /**
    	 *	分配一个指定字节大小的原生内存块
    	 *	内存内容是未初始化的;没有用的数据
    	 *	结果原生指针将不会是0,将会指派一个值。
         * 通过freeMemory方法处理掉(dispose)内容,或通过reallocateMemory方法重新调整大小
         * @throws IllegalArgumentException 如果大小是负数或原生size_t太大,将会抛异常。
         *
         * @throws OutOfMemoryError if the allocation is refused by the system 
    	 *							如果系统拒绝分配内存,则抛内存溢出错误
         * @see #getByte(long)
         * @see #putByte(long, byte)
         */
        public native long allocateMemory(long bytes);
    
        /**
    	 * 将指定内存块的所有字节设置为固定值(通常为0)
    	 * 这个方法通过两个参数决定一个块的基本地址,所以它提供了一个双寄存器寻址模式addressing mode,
    	 * 正如getInt(Object,long)中所讨论的。当对象引用为null时,offset提供了(supply)一个绝对地址。
         * 
         * 存储内容是连续的单元,大小由地址和参数长度决定的,如果地址有效且能被8取模,存储内容用long单元;
    	 * 如果地址有效且能被4或2取模,就用int 或short取代。
         * @since 1.7
         */
        public native void setMemory(Object o, long offset, long bytes, byte value);
        /**
         * Sets all bytes in a given block of memory to a fixed value (usually zero).
    	 * 将指定内存块的所有字节设置为固定值(通常为0)
    	 * This provides a single-register addressing mode,as discussed in #getInt(Object,long).
    	 * 这个方法提供了单寄存器寻址模式
         * 相同于 Equivalent to setMemory(null, address, bytes, value).
         */
        public void setMemory(long address, long bytes, byte value) {
            setMemory(null, address, bytes, value);
        }
    
        /**
         * 复制内存块,这个方法提供了双寄存器寻址模式,当对象引用为null时,offset表示绝对地址。
         * 这种复制在由大小确定的内存连续单元进行。
         * @since 1.7
         */
        public native void copyMemory(Object srcBase, long srcOffset,
                                      Object destBase, long destOffset,
                                      long bytes);
        /**
         * 复制内存块,这个方法提供了单寄存器寻址模式
         * Equivalent to <code>copyMemory(null, srcAddress, null, destAddress, bytes)</code>.
         */
        public void copyMemory(long srcAddress, long destAddress, long bytes) {
            copyMemory(null, srcAddress, null, destAddress, bytes);
        }
    
        /**
         * 将从#allocateMemory,#reallocateMemory获得obtained的原生native内存块清除dispose掉。
    	 * address为null时,不做任何处理。
         * @see #allocateMemory
         */
        public native void freeMemory(long address);
    
        /// random queries
    
        /**
         * This constant differs from all results that will ever be returned from
         * {@link #staticFieldOffset}, {@link #objectFieldOffset},
         * or {@link #arrayBaseOffset}.
         */
        public static final int INVALID_FIELD_OFFSET   = -1;
    
        /**
         * Returns the base address for accessing some static field in the given class. 
    	 * 为访问静态变量而返回指定类的基本地址
    	 */
        @Deprecated
        public Object staticFieldBase(Class<?> c) {
            Field[] fields = c.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                if (Modifier.isStatic(fields[i].getModifiers())) {
                    return staticFieldBase(fields[i]);
                }
            }
            return null;
        }
    
        /**
         * Report the location of a given static field, in conjunction with {@link #staticFieldBase}.
    	 * 报告一个指定静态属性的位置,和#staticFieldBase配合执行。
         * <p>Do not expect to perform any sort of arithmetic on this offset;
    	 * 不要指望在这个偏移量上执行任何类型的运算。
         * it is just a cookie which is passed to the unsafe heap memory accessors.
         * 它只是一个cookie被传递到不安全的堆内存访问 
         * 任何给定字段总是具有相同的偏移量,同一类的两个不同字段不会有相同的偏移量。
         *
         * <p>As of 1.4.1, offsets for fields are represented as long values,
    	 * 虽然SUN JVM不使用最有意义的32位
         * However, JVM implementations which store static fields at absolute
         * addresses can use long offsets and null base pointers to express the field locations in a form usable by {@link #getInt(Object,long)}.
         * 然而,将静态属性存储在绝对地址的JVM版本可以使用长类型偏移和空的基本指针来表示变量位置
    	 * Therefore, code which will be ported to such JVMs on 64-bit platforms must preserve all bits of static field offsets.
    	 * 所以,代码将被移植到64位平台JVM在上必须保留所有位静态字段偏移量 
    	 * @see #getInt(Object, long)
         */
        public native long staticFieldOffset(Field f);
    
        /**
    	 * 报告一个指定静态属性的位置,和#staticFieldBase配合执行。
    	 * 不要指望在这个偏移量上执行任何类型的运算。
         * 它只是一个cookie被传递到不安全的堆内存访问 
         * 任何给定字段总是具有相同的偏移量,同一类的两个不同字段不会有相同的偏移量。 
    	 * JDK1.4.1,字段的偏移值是long类型的值
    	 * 虽然SUN JVM不使用最有意义的32位
         * 很难想象一个JVM技术需要超过几个位来对非数组对象中的偏移进行编码,
    	 * 但是,为了与该类中的其他方法一致,该方法将其结果报告为一个长值。
         * @see #getInt(Object, long)
         */
        public native long objectFieldOffset(Field f);
    
        /**
         * Report the size in bytes of a native pointer, as stored via {@link
         * #putAddress}.  This value will be either 4 or 8.  Note that the sizes of
         * other primitive types (as stored in native memory blocks) is determined
         * fully by their information content.
         */
        public native int addressSize();
    
        /** The value of {@code addressSize()} */
        public static final int ADDRESS_SIZE = theUnsafe.addressSize();
    
        /**
         * Report the size in bytes of a native memory page (whatever that is).
         * This value will always be a power of two.
         */
        public native int pageSize();
    
    
        /// random trusted operations from JNI:
    
        /**
         * Tell the VM to define a class, without security checks.  By default, the
         * class loader and protection domain come from the caller's class.
         */
        public native Class<?> defineClass(String name, byte[] b, int off, int len,
                                           ClassLoader loader,
                                           ProtectionDomain protectionDomain);
    
        /** Allocate an instance but do not run any constructor.
            Initializes the class if it has not yet been. */
        public native Object allocateInstance(Class<?> cls)
            throws InstantiationException;
    		
    	/** 锁住对象,必须通过monitorExit解锁. */
        public native void monitorEnter(Object o);
        /**
    	 * 解锁对象,必须已经通过#monitorEnter锁住
         */
        public native void monitorExit(Object o);
    
        /**
    	 * 尝试锁住对象,返回值true或false表示是否锁住。如果锁住,必须通过#monitorExit解锁。
         */
        public native boolean tryMonitorEnter(Object o);
    
        /** Throw the exception without telling the verifier. */
        public native void throwException(Throwable ee);
        /**
         * Atomically update Java variable to <tt>x</tt> if it is currently
         * holding <tt>expected</tt>.
    	 * 如果对象是期待值,原子性更新Java变量为x
         * @return <tt>true</tt> if successful
         */
        public final native boolean compareAndSwapObject(Object o, long offset,
                                                         Object expected,
                                                         Object x);
    
        /**
         * Unblock the given thread blocked on <tt>park</tt>, or, if it is
         * not blocked, cause the subsequent call to <tt>park</tt> not to
         * block.  Note: this operation is "unsafe" solely because the
         * caller must somehow ensure that the thread has not been
         * destroyed. Nothing special is usually required to ensure this
         * when called from Java (in which there will ordinarily be a live
         * reference to the thread) but this is not nearly-automatically
         * so when calling from native code.
         * @param thread the thread to unpark.
         *
         */
        public native void unpark(Object thread);
    
        /**
         * Block current thread, returning when a balancing
         * <tt>unpark</tt> occurs, or a balancing <tt>unpark</tt> has
         * already occurred, or the thread is interrupted, or, if not
         * absolute and time is not zero, the given time nanoseconds have
         * elapsed, or if absolute, the given deadline in milliseconds
         * since Epoch has passed, or spuriously (i.e., returning for no
         * "reason"). Note: This operation is in the Unsafe class only
         * because <tt>unpark</tt> is, so it would be strange to place it
         * elsewhere.
         */
        public native void park(boolean isAbsolute, long time);
    
        // The following contain CAS-based Java implementations used on
        // platforms not supporting native instructions
    
        /**
         * Atomically adds the given value to the current value of a field
         * or array element within the given object <code>o</code>
         * at the given <code>offset</code>.
         *
         * @param o object/array to update the field/element in
         * @param offset field/element offset
         * @param delta the value to add
         * @return the previous value
         * @since 1.8
         */
        public final int getAndAddInt(Object o, long offset, int delta) {
            int v;
            do {
                v = getIntVolatile(o, offset);
            } while (!compareAndSwapInt(o, offset, v, v + delta));
            return v;
        }
    
        /**
         * Atomically exchanges the given value with the current value of
         * a field or array element within the given object <code>o</code>
         * at the given <code>offset</code>.
         *
         * @param o object/array to update the field/element in
         * @param offset field/element offset
         * @param newValue new value
         * @return the previous value
         * @since 1.8
         */
        public final int getAndSetInt(Object o, long offset, int newValue) {
            int v;
            do {
                v = getIntVolatile(o, offset);
            } while (!compareAndSwapInt(o, offset, v, newValue));
            return v;
        }
        /**
         * Ensures lack of reordering of loads before the fence
         * with loads or stores after the fence.
         * @since 1.8
         */
        public native void loadFence();
        public native void storeFence();
        public native void fullFence();
    
    }

    AtomicLong

    然后,那我们以AtomicLong为例,看JDK中是如何使用Unsafe的。

    package java.util.concurrent.atomic;
    import sun.misc.Unsafe;
    
    /**
     * 一个AtomicLong被用作原子性增加的序列数字,不是Long的替代,
     * 这个类的确继承了Number,允许使用工具类按接口获得实际数值。
     * @since 1.5
     * @author Doug Lea
     */
    public class AtomicLong extends Number implements java.io.Serializable {
        private static final long serialVersionUID = 1927816293512124184L;
    
        // 这种声明方式,是sun官方推荐的、被信任的。我们在开发时就可以这样使用Unsafe
        private static final Unsafe unsafe = Unsafe.getUnsafe();
        private static final long valueOffset;
    
        /**
         * 记录虚拟机 是否支持 对Long类型进行无锁的compareAndSwap。
         * 无论虚拟机是否支持,一些构造器应该被控制在Java语言级别,避免显式锁的泄露。
         */
        static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
    
        /**
         * 记录底层虚拟机 是否支持 对Long类型进行无锁的CompareAndSet。
         * 这个方法只会调用一次,结果缓存在VM_SUPPORTS_LONG_CAS。
         */
        private static native boolean VMSupportsCS8();
    
        static {
          try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicLong.class.getDeclaredField("value"));
          } catch (Exception ex) { throw new Error(ex); }
        }
    
        private volatile long value;
    
        /**
         * 用给定的初始值创建AtomicLong实例
         * @param initialValue 初始值
         */
        public AtomicLong(long initialValue) {
            value = initialValue;
        }
    
        public AtomicLong() {
        }
    
        /**
         * 返回当前值
         * @return 当前值
         */
        public final long get() {
            return value;
        }
    
        /**
         * 设置给定值
         *
         * @param newValue 新值
         */
        public final void set(long newValue) {
            value = newValue;
        }
    
        /**
         * 最终设定值
         *
         * @param newValue the new value
         * @since 1.6
         */
        public final void lazySet(long newValue) {
            unsafe.putOrderedLong(this, valueOffset, newValue);
        }
    
        /**
         * 原子性设定新值,返回旧值
         * @param newValue 新值
         * @return 旧值
         */
        public final long getAndSet(long newValue) {
            while (true) {
                // 失败后,进行重试,get()返回值是未知的,即时获得的。并不是上次循环的旧值
                long current = get();
                if (compareAndSet(current, newValue))
                    return current;
            }
        }
    
        /**
         * 如果当前值==期待值,原子性地设置变量为给定更新值
         * @param 期待值
         * @param 要更新的新值
         * @return 如果成功返回true.失败则返回false,表明实际值不等于期待值
         */
        public final boolean compareAndSet(long expect, long update) {
            return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
        }
    
        /**
         * 如果当前值==期待值,设定变量为给定更新值
         * 可能假失败,不提供排序保证,所以,只是很少使用的选项。
         * @param expect 期待值
         * @param update 给定更新值
         * @return 成功则返回true
         */
        public final boolean weakCompareAndSet(long expect, long update) {
            return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
        }
    
        /**
         * 原子性递增当前值,返回当前值
         * @return the previous value
         */
        public final long getAndIncrement() {
            while (true) {
                // 失败后,进行重试,get()返回值是未知的,即时获得的。并不是上次循环的旧值
                long current = get(); 
                long next = current + 1;
                if (compareAndSet(current, next))
                    return current;
            }
        }
    
        /**
         * 原子性递减当前值,返回当前值
         *
         * @return the previous value
         */
        public final long getAndDecrement() {
            while (true) {
                // 失败后,进行重试,get()返回值是未知的,即时获得的。并不是上次循环的旧值
                long current = get(); 
                long next = current - 1;
                if (compareAndSet(current, next))
                    return current;
            }
        }
    
        /**
         * 原子性地将当前值与给定值相加,当前值是未知的,即时获得的。
         *
         * @param delta 加数
         * @return 先前值
         */
        public final long getAndAdd(long delta) {
            while (true) {
                // 失败后,进行重试,get()返回值是未知的,即时获得的。并不是上次循环的旧值
                long current = get();
                long next = current + delta;
                if (compareAndSet(current, next))
                    return current;
            }
        }
    
        /**
         * 原子性地递增值
         *
         * @return 更新值
         */
        public final long incrementAndGet() {
            for (;;) {
                long current = get();
                long next = current + 1;
                if (compareAndSet(current, next))
                    return next;
            }
        }
    
    }
    

      

      

  • 相关阅读:
    Git 基础
    SharePoint 2013 对象模型操作"网站设置"菜单
    SharePoint 2013 隐藏部分Ribbon菜单
    SharePoint 2013 Designer系列之数据视图筛选
    SharePoint 2013 Designer系列之数据视图
    SharePoint 2013 Designer系列之自定义列表表单
    SharePoint 2013 设置自定义布局页
    SharePoint 2013 "通知我"功能简介
    SharePoint 2013 创建web应用程序报错"This page can’t be displayed"
    SharePoint 禁用本地回环的两个方法
  • 原文地址:https://www.cnblogs.com/zhengwenqiang/p/8119590.html
Copyright © 2011-2022 走看看