Java --- Unsafe

来源:互联网 发布:c语言创建线程 编辑:程序博客网 时间:2024/06/05 09:23

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

     */

    privatevoidunparkSuccessor(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 (Nodet = 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<K,V> find(inth, Objectk) {

            if (k !=null) {

                for (Node<K,V>e = first;e != null;e = e.next) {

                    ints; Kek;

                    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<K,V>r, p;

                        try {

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

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

                        } finally {

                            Threadw;

                            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 inlw ^ 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));

          }

        }

 

        // Theepilogue 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.UnsafegetUnsafe() {

        try {

            return sun.misc.Unsafe.getUnsafe();

        } catch (SecurityExceptiontryReflectionInstead) {}

        try {

            return java.security.AccessController.doPrivileged

                    (new java.security.PrivilegedExceptionAction<sun.misc.Unsafe>() {

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

                            Class<sun.misc.Unsafe>k = sun.misc.Unsafe.class;

                            for (java.lang.reflect.Fieldf : k.getDeclaredFields()) {

                                f.setAccessible(true);

                                Object x = f.get(null);

                                if (k.isInstance(x))

                                    returnk.cast(x);

                            }

                            thrownew NoSuchFieldError("the Unsafe");

                        }});

        } catch (java.security.PrivilegedActionExceptione) {

            thrownew RuntimeException("Could not initialize intrinsics",

                    e.getCause());

        }

    }

 

0 0