代码改变世界

sun.misc.Unsafe

2013-02-27 21:46  ggzwtj  阅读(2439)  评论(0编辑  收藏  举报

Java不能直接访问操作系统底层,而是通过本地方法来访问,Unsafe提供了硬件级别的原子操作,提供了以下功能:

分配、释放内存

操作内存主要依靠下面三个方法:

  1. allocateMemory:分配内存;
  2. reallocateMemory:扩充内存;
  3. freeMemory:释放内存;

操作对象的字段

  Java对象中字段的定位可以通过staticFieldOffset实现,而读取具体便宜位置的字段的值可以使用getLong(根据不同类型选择不同的函数)来完成,可以使用putLong(根据不同的类型选择不同的函数)来设置值,可以使用arrayBaseOffset(获取数组第一个元素的偏移地址)和arrayIndexScale(获取数组中元素增量的地址)来访问数组中的元素。

CAS操作

CAS操作(会在java.util.concurrent中大量地使用)包括三个操作数:

  1. 内存位置;
  2. 预期原值;
  3. 新值;

如果内存位置的值与预期的值相等,那么处理器会将该位置的值设置为新值,否则,处理器不做任何处理。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