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<K,V> find(inth, Object k) { if (k != null) { for (Node<K,V> 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<K,V> 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<sun.misc.Unsafe>() { public sun.misc.Unsafe run() throws Exception { Class<sun.misc.Unsafe> 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()); } } |