Java --- Unsafe

Java --- Unsafe

初步介绍

在AQS,Netty和Guava的源码中出现了sun.misc.Unsafe 的身影。Unsafe类的定义是:执行底层,不安全的操作的方法的集合,所有的方法都是Native的。主要分为以下几类方法:

内存操作:

addressSize,allocateMemory,copyMemory,copyMemory,freeMemory,getAddress,putAddress,reallocateMemory,setMemory,setMemory,pageSize,

实例域或静态域操作:

fieldOffset,getBoolean,getBoolean,getBooleanVolatile,getByte,getByte,getByte,getByteVolatile,getChar,getChar,getChar,getCharVolatile,getDouble,getDouble,getDouble,getDoubleVolatile,getFloat,getFloat,getFloat,getFloatVolatile,getInt,getInt,getInt,getIntVolatile,getLong,getLong,getLong,getLongVolatile,getObject,getObject,getObjectVolatile,getShort,getShort,getShort,getShortVolatile,objectFieldOffset,putBoolean,putBoolean,putBooleanVolatile,putByte,putByte,putByte,putByteVolatile,putChar,putChar,putChar,putCharVolatile,putDouble,putDouble,putDouble,putDoubleVolatile,putFloat,putFloat,putFloat,putFloatVolatile,putInt,putInt,putInt,putIntVolatile,putLong,putLong,putLong,putLongVolatile,putObject,putObject,putObjectVolatile,putOrderedInt,putOrderedLong,putOrderedObject,putShort,putShort,putShort,putShortVolatile,staticFieldBase,staticFieldBase,staticFieldOffset,compareAndSwapInt,compareAndSwapLong,compareAndSwapObject,

线程操作:

         Park,unpark

类和对象操作:

defineAnonymousClass,defineClass,defineClass,ensureClassInitialized,allocateInstance,

其它(对象锁,获取负载均衡…):

monitorEnter,monitorExit,tryMonitorEnter,getLoadAverage,getUnsafe,throwException,arrayBaseOffset,arrayIndexScale,

 

这里把所有方法大概的列了一下,具体方法说明可以参见:

http://www.docjar.com/docs/api/sun/misc/Unsafe.html#monitorEnter%28Object%29

 

看了所有的方法后,可以发现操作大都是直接绕过虚拟机的规范限制而直接操作内存,这样没有了保护当然是Unsafe的了,但又一个重点是native,对于native的调用在字节码层面就是一个字节码,如果我们用Java语言来操作上面的比如域赋值等操作的话,最终的字节码是不止一个的,这样在多线程环境下就必须要进行同步保护了。

 

AQS中的Unsafe

         AQS作为作为排外和共享锁的一个通用基类,再起FIFO队列操作中大量使用了Unsafe的compareAndSwapXXX方法进行队列操作,利用native方法的原子性来保证了线程安全。

/**

     * CAS waitStatus field of a node.

     */

    privatestaticfinalboolean compareAndSetWaitStatus(Node node,

                                                         intexpect,

                                                         intupdate) {

        returnunsafe.compareAndSwapInt(node, waitStatusOffset,

                                        expect, update);

    }

 

    /**

     * CAS next field of a node.

     */

    privatestaticfinalboolean compareAndSetNext(Node node,

                                                   Node expect,

                                                   Node update) {

        returnunsafe.compareAndSwapObject(node, nextOffset, expect, update);

    }

 

         另外对堵塞等待的线程使用了unsafe的park和unpark来使得线程被挂起或获得执行的权限。

/**

     * Wakes up node's successor, if one exists.

     *

     * @param node the node

     */

    privatevoid unparkSuccessor(Node node) {

        /*

         * If status is negative (i.e., possibly needing signal) try

         * to clear in anticipation of signalling.  It is OK if this

         * fails or if status is changed by waiting thread.

         */

        intws = node.waitStatus;

        if (ws < 0)

            compareAndSetWaitStatus(node, ws, 0);

 

        /*

         * Thread to unpark is held in successor, which is normally

         * just the next node.  But if cancelled or apparently null,

         * traverse backwards from tail to find the actual

         * non-cancelled successor.

         */

        Node s = node.next;

        if (s == null || s.waitStatus > 0) {

            s = null;

            for (Node t = tail; t != null && t != node; t = t.prev)

                if (t.waitStatus <= 0)

                    s = t;

        }

        if (s != null)

            LockSupport.unpark(s.thread);

}

 

  /**

     * Convenience method to park and then check if interrupted

     *

     * @return {@code true} if interrupted

     */

    privatefinalboolean parkAndCheckInterrupt() {

        LockSupport.park(this);

        return Thread.interrupted();

}

 

另外JDK concurrent的automD

 

Netty中的Unsafe

         在Netty使用了Unsafe操作来作为原子操作。

         Io.netty.util.inernal.chmv8.ConcurrentHashMapV8是一个线程安全的HashMap,代码中多次使用了Unsafe的compareAndSwapXXX方法来作为原子操作,提供线程安全性。

final Node find(inth, Object k) {

            if (k != null) {

                for (Node e = first; e != null; e = e.next) {

                    ints; K ek;

                    if (((s = lockState) & (WAITER|WRITER)) != 0) {

                        if (e.hash == h &&

                                ((ek = e.key) == k || (ek != null && k.equals(ek))))

                            returne;

                    }

                    elseif (U.compareAndSwapInt(this, LOCKSTATE, s,

                            s + READER)) {

                        TreeNode r, p;

                        try {

                            p = ((r = root) == null ? null :

                                    r.findTreeNode(h, k, null));

                        } finally {

                            Thread w;

                            intls;

                            do {} while (!U.compareAndSwapInt

                                    (this, LOCKSTATE,

                                            ls = lockState, ls - READER));

                            if (ls == (READER|WAITER) && (w = waiter) != null)

                                LockSupport.unpark(w);

                        }

                        returnp;

                    }

                }

            }

            returnnull;

        }

 

Guava的unsafe

         Guava中的com.google.common.primitives.UnsignedByte类给我们展示了除了常用来作为原子操作的之外的另一个作用,将字符数组的比较转换为一次性比较8个字符的比较方法。

@Overridepublicint compare(byte[] left, byte[] right) {

        intminLength = Math.min(left.length, right.length);

        intminWords = minLength / Longs.BYTES;

 

        /*

         * Compare 8 bytes at a time. Benchmarking shows comparing 8 bytes at a

         * time is no slower than comparing 4 bytes at a time even on 32-bit.

         * On the other hand, it is substantially faster on 64-bit.

         */

        for (inti = 0; i < minWords * Longs.BYTES; i += Longs.BYTES) {

          longlw = theUnsafe.getLong(left, BYTE_ARRAY_BASE_OFFSET + (long) i);

          longrw = theUnsafe.getLong(right, BYTE_ARRAY_BASE_OFFSET + (long) i);

          if (lw != rw) {

            if (BIG_ENDIAN) {

              return UnsignedLongs.compare(lw, rw);

            }

 

            /*

             * We want to compare only the first index where left[index] != right[index].

             * This corresponds to the least significant nonzero byte in lw ^ rw, since lw

             * and rw are little-endian.  Long.numberOfTrailingZeros(diff) tells us the least

             * significant nonzero bit, and zeroing out the first three bits of L.nTZ gives us the

             * shift to get that least significant nonzero byte.

             */

            intn = Long.numberOfTrailingZeros(lw ^ rw) & ~0x7;

            return (int) (((lw >>> n) & UNSIGNED_MASK) - ((rw >>> n) & UNSIGNED_MASK));

          }

        }

 

        // The epilogue to cover the last (minLength % 8) elements.

        for (inti = minWords * Longs.BYTES; i < minLength; i++) {

          intresult = UnsignedBytes.compare(left[i], right[i]);

          if (result != 0) {

            returnresult;

          }

        }

        returnleft.length - right.length;

      }

    }

 

 

需要使用Unsafe类吗

         从这些源码库的代码中可以看出sun.misc.Unsafe类是一个非常强大的类,但必须意识到这同时是一个非常危险的类。作为个人基本原则是不直接使用该类(因为我们使用这些开源类库的同时也在间接使用了该类的强大功能,同时也避免了其不安全因素)。

 

Unsafe实例的获取方法:

privatestatic sun.misc.Unsafe getUnsafe() {

        try {

            return sun.misc.Unsafe.getUnsafe();

        } catch (SecurityException tryReflectionInstead) {}

        try {

            return java.security.AccessController.doPrivileged

                    (new java.security.PrivilegedExceptionAction() {

                        public sun.misc.Unsafe run() throws Exception {

                            Class k = sun.misc.Unsafe.class;

                            for (java.lang.reflect.Field f : k.getDeclaredFields()) {

                                f.setAccessible(true);

                                Object x = f.get(null);

                                if (k.isInstance(x))

                                    returnk.cast(x);

                            }

                            thrownew NoSuchFieldError("the Unsafe");

                        }});

        } catch (java.security.PrivilegedActionException e) {

            thrownew RuntimeException("Could not initialize intrinsics",

                    e.getCause());

        }

    }

 

你可能感兴趣的:(java)