sun.misc.Unsafe
2013-02-27 21:46 ggzwtj 阅读(2438) 评论(0) 编辑 收藏 举报Java不能直接访问操作系统底层,而是通过本地方法来访问,Unsafe提供了硬件级别的原子操作,提供了以下功能:
分配、释放内存
操作内存主要依靠下面三个方法:
- allocateMemory:分配内存;
- reallocateMemory:扩充内存;
- freeMemory:释放内存;
操作对象的字段
Java对象中字段的定位可以通过staticFieldOffset实现,而读取具体便宜位置的字段的值可以使用getLong(根据不同类型选择不同的函数)来完成,可以使用putLong(根据不同的类型选择不同的函数)来设置值,可以使用arrayBaseOffset(获取数组第一个元素的偏移地址)和arrayIndexScale(获取数组中元素增量的地址)来访问数组中的元素。
CAS操作
CAS操作(会在java.util.concurrent中大量地使用)包括三个操作数:
- 内存位置;
- 预期原值;
- 新值;
如果内存位置的值与预期的值相等,那么处理器会将该位置的值设置为新值,否则,处理器不做任何处理。int类型的CAS实现如下:
static inline bool compareAndSwap (volatile jint *addr, jint old, jint new_val) { jboolean result = false; spinlock lock; if ((result = (*addr == old))) *addr = new_val; return result; }
这段代码看起来根本无法保证操作的原子性,而其中的关键就在于spinlock lock这段看似没有用的代码,这时会调用构造函数,其中不停检查lock的值是否为0,如果不是0则线程让步等到线程下次执行时再次检查;否则,将lock设置为1。在离开函数的时候会调用其析构函数,将lock的值再次设置为0。spinlock的代码如下:
class spinlock { static volatile obj_addr_t lock; public: spinlock () { while (!compare_and_swap(&lock, 0, 1)) _Jv_ThreadYield(); } ~spinlock () { release_set(&lock, 0); } };
而且,不管是在多核还是在单核的情况下,总是需要一些特殊的手段才能保证CAS操作成功吧,比如锁总线,compare_and_swap的实现如下:
inline static bool compare_and_swap(volatile obj_addr_t *addr, obj_addr_t old, obj_addr_t new_val) { char result; #ifdef __x86_64__ __asm__ __volatile__("lock; cmpxchgq %2, %0; setz %1" : "=m"(*(addr)), "=q"(result) : "r" (new_val), "a"(old), "m"(*addr) : "memory"); #else __asm__ __volatile__("lock; cmpxchgl %2, %0; setz %1" : "=m"(*addr), "=q"(result) : "r" (new_val), "a"(old), "m"(*addr) : "memory"); #endif return (bool) result; }
挂起和恢复
使用park方法将一个线程挂起,知道超时或者中断等条件出现,实现如下:
void sun::misc::Unsafe::park(jboolean isAbsolute, jlong time) { using namespace ::java::lang; Thread *thread = Thread::currentThread(); natThread *nt = (natThread *) thread->data; nt->park_helper.park(isAbsolute, time); }
使用unpark来终止一个挂起的线程,使其恢复正常,实现如下:
sun::misc::Unsafe::unpark (::java::lang::Thread *thread) { natThread *nt = (natThread *) thread->data; nt->park_helper.unpark (); }
----- -- -
END