Java 并发随身记(一)之 Unsafe类

  最近在看Java并发相关的内容,需要自己整理整理,不然就生疏了。工作2年多,工作时一般注都是框架、消息这些内容,对基础内容比较忽视。闲话不说,既然是并发内容,首先先复习一下Unsafe的内容吧。

Unsafe 类提供了硬件级别的原子操作,它提供非常有趣的一些内容。首先我们看下Unsafe文件。并对其一些内容进行分析,然后给出具体的用法。网上找了一个部分的源代码,看了一下。基本已经达到我们分享的要求。

/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package sun.misc;

import dalvik.system.VMStack;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/**
 * The package name notwithstanding, this class is the quasi-standard
 * way for Java code to gain access to and use functionality which,
 * when unsupervised, would allow one to break the pointer/type safety
 * of Java.
 * <p>
 * 该包名不具有代表性.可以通过该类来获取和访问数据,但是容易破坏Java指针和类型的安全性。
 */
public final class Unsafe {
    /**
     * Traditional dalvik name.
     * 内部维护一个静态的 Unsafe 实例
     */
    private static final Unsafe THE_ONE = new Unsafe();
    /**
     * Traditional RI name.
     */
    private static final Unsafe theUnsafe = THE_ONE;

    /**
     * This class is only privately instantiable.
     * <p>
     * 说明该类不能被new 出来
     */
    private Unsafe() {
    }

    /**
     * Gets the unique instance of this class. This is only allowed in
     * very limited situations.
     * <p>
     * 静态方法,通过getUnsafe方法获取一个Unsafe 实例
     */
    public static Unsafe getUnsafe() {
        /*
         * Only code on the bootclasspath is allowed to get at the
         * Unsafe instance.
         *
         * 只有在 bootclasspath下的Java类才能获取得到该实例,否则抛异常。
         */
        ClassLoader calling = VMStack.getCallingClassLoader();
        if ((calling != null) && (calling != Unsafe.class.getClassLoader())) {
            throw new SecurityException("Unsafe access denied");
        }
        return THE_ONE;
    }

    /**
     * Gets the raw byte offset from the start of an object's memory to
     * the memory used to store the indicated instance field.
     * <p>
     * 获取从对象内存开头到用于存储指示实例字段的内存的原始字节偏移量。也就是
     * 获取某对象内的变量的相对地址偏移量
     *
     * @param field non-null; the field in question, which must be an
     *              instance field
     * @return the offset to the field
     */
    public long objectFieldOffset(Field field) {
        if (Modifier.isStatic(field.getModifiers())) {
            throw new IllegalArgumentException(
                    "valid for instance fields only");
        }
        return objectFieldOffset0(field);
    }

    /**
     * Helper for {@link #objectFieldOffset}, which does all the work,
     * assuming the parameter is deemed valid.
     * <p>
     * 本地方法,获取对象属性的偏移量
     *
     * @param field non-null; the instance field
     * @return the offset to the field
     */
    private static native long objectFieldOffset0(Field field);

    /**
     * Gets the offset from the start of an array object's memory to
     * the memory used to store its initial (zeroeth) element.
     * <p>
     * 获取数组对象的起始位置
     *
     * @param clazz non-null; class in question; must be an array class
     * @return the offset to the initial element
     */
    public int arrayBaseOffset(Class clazz) {
        if (!clazz.isArray()) {
            throw new IllegalArgumentException(
                    "valid for array classes only");
        }
        return arrayBaseOffset0(clazz);
    }

    /**
     * Helper for {@link #arrayBaseOffset}, which does all the work,
     * assuming the parameter is deemed valid.
     *
     * @return the offset to the field
     */
    private static native int arrayBaseOffset0(Class clazz);

    /**
     * Gets the size of each element of the given array class.
     * <p>
     * 获取给定数组类的每个元素的大小
     *
     * @param clazz non-null; class in question; must be an array class
     * @return &gt; 0; the size of each element of the array
     */
    public int arrayIndexScale(Class clazz) {
        if (!clazz.isArray()) {
            throw new IllegalArgumentException(
                    "valid for array classes only");
        }
        return arrayIndexScale0(clazz);
    }

    /**
     * Helper for {@link #arrayIndexScale}, which does all the work,
     * assuming the parameter is deemed valid.
     *
     * @return the offset to the field
     */
    private static native int arrayIndexScale0(Class clazz);

    /**
     * Performs a compare-and-set operation on an <code>int</code>
     * field within the given object.
     * <p>
     * 比较置换操作(CAS)for int
     *
     * @param obj           non-null; object containing the field
     * @param offset        offset to the field within <code>obj</code>
     * @param expectedValue expected value of the field
     * @param newValue      new value to store in the field if the contents are
     *                      as expected
     * @return <code>true</code> if the new value was in fact stored, and
     * <code>false</code> if not
     */
    public native boolean compareAndSwapInt(Object obj, long offset,
                                            int expectedValue, int newValue);

    /**
     * Performs a compare-and-set operation on a <code>long</code>
     * field within the given object.
     * <p>
     * 比较置换操作(CAS)for long
     *
     * @param obj           non-null; object containing the field
     * @param offset        offset to the field within <code>obj</code>
     * @param expectedValue expected value of the field
     * @param newValue      new value to store in the field if the contents are
     *                      as expected
     * @return <code>true</code> if the new value was in fact stored, and
     * <code>false</code> if not
     */
    public native boolean compareAndSwapLong(Object obj, long offset,
                                             long expectedValue, long newValue);

    /**
     * Performs a compare-and-set operation on an <code>Object</code>
     * field (that is, a reference field) within the given object.
     * <p>
     * 比较置换操作(CAS) for object
     *
     * @param obj           non-null; object containing the field
     * @param offset        offset to the field within <code>obj</code>
     * @param expectedValue expected value of the field
     * @param newValue      new value to store in the field if the contents are
     *                      as expected
     * @return <code>true</code> if the new value was in fact stored, and
     * <code>false</code> if not
     */
    public native boolean compareAndSwapObject(Object obj, long offset,
                                               Object expectedValue, Object newValue);

    /**
     * Gets an <code>int</code> field from the given object,
     * using <code>volatile</code> semantics.
     * 获取 对象 obj 偏移量offset 的值 (int)
     *
     * @param obj    non-null; object containing the field
     * @param offset offset to the field within <code>obj</code>
     * @return the retrieved value
     */
    public native int getIntVolatile(Object obj, long offset);

    /**
     * Stores an <code>int</code> field into the given object,
     * using <code>volatile</code> semantics.
     * <p>
     * 把值newValue 放到对象obj 偏移量为offset 上 (int)
     *
     * @param obj      non-null; object containing the field
     * @param offset   offset to the field within <code>obj</code>
     * @param newValue the value to store
     */
    public native void putIntVolatile(Object obj, long offset, int newValue);

    /**
     * Gets a <code>long</code> field from the given object,
     * using <code>volatile</code> semantics.
     * <p>
     * 获取 对象 obj 偏移量offset 的值 (long)
     *
     * @param obj    non-null; object containing the field
     * @param offset offset to the field within <code>obj</code>
     * @return the retrieved value
     */
    public native long getLongVolatile(Object obj, long offset);

    /**
     * Stores a <code>long</code> field into the given object,
     * using <code>volatile</code> semantics.
     * <p>
     * 把值newValue 放到对象obj 偏移量为offset 上 (long)
     *
     * @param obj      non-null; object containing the field
     * @param offset   offset to the field within <code>obj</code>
     * @param newValue the value to store
     */
    public native void putLongVolatile(Object obj, long offset, long newValue);

    /**
     * Gets an <code>Object</code> field from the given object,
     * using <code>volatile</code> semantics.
     * 获取 对象 obj 偏移量offset 的值 (object)
     *
     * @param obj    non-null; object containing the field
     * @param offset offset to the field within <code>obj</code>
     * @return the retrieved value
     */
    public native Object getObjectVolatile(Object obj, long offset);

    /**
     * Stores an <code>Object</code> field into the given object,
     * using <code>volatile</code> semantics.
     * <p>
     * 把值newValue 放到对象obj 偏移量为offset 上 (object)
     *
     * @param obj      non-null; object containing the field
     * @param offset   offset to the field within <code>obj</code>
     * @param newValue the value to store
     */
    public native void putObjectVolatile(Object obj, long offset,
                                         Object newValue);

    /**
     * Gets an <code>int</code> field from the given object.
     *
     * @param obj    non-null; object containing the field
     * @param offset offset to the field within <code>obj</code>
     * @return the retrieved value
     */
    public native int getInt(Object obj, long offset);

    /**
     * Stores an <code>int</code> field into the given object.
     *
     * @param obj      non-null; object containing the field
     * @param offset   offset to the field within <code>obj</code>
     * @param newValue the value to store
     */
    public native void putInt(Object obj, long offset, int newValue);

    /**
     * Lazy set an int field.
     */
    public native void putOrderedInt(Object obj, long offset, int newValue);

    /**
     * Gets a <code>long</code> field from the given object.
     *
     * @param obj    non-null; object containing the field
     * @param offset offset to the field within <code>obj</code>
     * @return the retrieved value
     */
    public native long getLong(Object obj, long offset);

    /**
     * Stores a <code>long</code> field into the given object.
     *
     * @param obj      non-null; object containing the field
     * @param offset   offset to the field within <code>obj</code>
     * @param newValue the value to store
     */
    public native void putLong(Object obj, long offset, long newValue);

    /**
     * Lazy set a long field.
     */
    public native void putOrderedLong(Object obj, long offset, long newValue);

    /**
     * Gets an <code>Object</code> field from the given object.
     *
     * @param obj    non-null; object containing the field
     * @param offset offset to the field within <code>obj</code>
     * @return the retrieved value
     */
    public native Object getObject(Object obj, long offset);

    /**
     * Stores an <code>Object</code> field into the given object.
     *
     * @param obj      non-null; object containing the field
     * @param offset   offset to the field within <code>obj</code>
     * @param newValue the value to store
     */
    public native void putObject(Object obj, long offset, Object newValue);

    /**
     * Lazy set an object field.
     */
    public native void putOrderedObject(Object obj, long offset,
                                        Object newValue);

    /**
     * Parks the calling thread for the specified amount of time,
     * unless the "permit" for the thread is already available (due to
     * a previous call to {@link #unpark}. This method may also return
     * spuriously (that is, without the thread being told to unpark
     * and without the indicated amount of time elapsing).
     * <p>
     * <p>See {@link java.util.concurrent.locks.LockSupport} for more
     * in-depth information of the behavior of this method.</p>
     * <p>
     * 除非当前线程已经可用(比如调用unpark),否则挂起当前线程time 时间,
     *
     * @param absolute whether the given time value is absolute
     *                 milliseconds-since-the-epoch (<code>true</code>) or relative
     *                 nanoseconds-from-now (<code>false</code>)
     * @param time     the (absolute millis or relative nanos) time value
     */
    public void park(boolean absolute, long time) {
        if (absolute) {
            Thread.currentThread().parkUntil(time);
        } else {
            Thread.currentThread().parkFor(time);
        }
    }

    /**
     * Unparks the given object, which must be a {@link Thread}.
     * <p>
     * 恢复指定线程
     * <p>
     * <p>See {@link java.util.concurrent.locks.LockSupport} for more
     * in-depth information of the behavior of this method.</p>
     *
     * @param obj non-null; the object to unpark
     */
    public void unpark(Object obj) {
        if (obj instanceof Thread) {
            ((Thread) obj).unpark();
        } else {
            throw new IllegalArgumentException("valid for Threads only");
        }
    }

    /**
     * Allocates an instance of the given class without running the constructor.
     * <p>
     * 不通过构造器,分配一个类的实例
     * The class' <clinit> will be run, if necessary.
     */
    public native Object allocateInstance(Class<?> c);
}

  从上面的注释,大概可以看出Unsafe都干了什么事情,其实它干的事情就如C语言操作数据地址。接下来我们需要如何使用提供的Api.

首先,我们无法通过Unsafe.getUnsafe()方法来创建一个Unsafe实例,因为他需要时bootclasspath下的类才能使用它,所以我们需要通过反射的方法得到这个类的对象。代码如下:

 public static Unsafe getUnsafe() {
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            Unsafe unsafe = (Unsafe) theUnsafe.get(null);
            return unsafe;
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

  这样我们就得到了这个Unsafe对象,接下来我们来看下如何以上的方法。下面的栗子看起来比较鸡肋,但是它可以帮助我们如何使用以上的api.主要是向类型为User的对象不通过get,set方法来获取,设置值。而是通过Unsafe类。

工具类(GASUtils)

package com.qee.unsafe;

import sun.misc.Unsafe;

import java.lang.reflect.Field;


public class GASUtils {

    private static final Unsafe UNSAFE = getUnsafe();

    private static Unsafe getUnsafe() {
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            Unsafe unsafe = (Unsafe) theUnsafe.get(null);
            return unsafe;
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取一个对象属性值(该属性是int 类型)
     *
     * @param object
     * @param paramName
     * @return
     */
    public static Integer getInt(Object object, String paramName) {

        try {
            long offset = UNSAFE.objectFieldOffset(object.getClass().getDeclaredField(paramName));
            return UNSAFE.getIntVolatile(object, offset);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        return null;

    }


    /**
     * 设置一个对象属性值(该属性是int 类型)
     *
     * @param object
     * @param paramName
     * @param value
     */
    public static void setInt(Object object, String paramName, int value) {

        try {
            long offset = UNSAFE.objectFieldOffset(object.getClass().getDeclaredField(paramName));
            UNSAFE.putIntVolatile(object, offset, value);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取一个对象属性值(该属性是Object 类型)
     *
     * @param object
     * @param paramName
     * @return
     */
    public static Object getObject(Object object, String paramName) {
        try {
            long offset = UNSAFE.objectFieldOffset(object.getClass().getDeclaredField(paramName));
            return UNSAFE.getObjectVolatile(object, offset);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 设置一个对象属性值(该属性是Object 类型)
     *
     * @param object
     * @param paramName
     * @param value
     */
    public static void setObject(Object object, String paramName, Object value) {
        try {
            long offset = UNSAFE.objectFieldOffset(object.getClass().getDeclaredField(paramName));
            UNSAFE.putObjectVolatile(object, offset, value);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

    }

    /**
     * cas 比较设置,返回对象里面的值(Int)
     *
     * @param object
     * @param paramName
     * @param expectValue
     * @param updateValue
     * @return
     */
    public static int compareAndSetInt(Object object, String paramName, int expectValue, int updateValue) {
        try {
            long offset = UNSAFE.objectFieldOffset(object.getClass().getDeclaredField(paramName));
            boolean b = UNSAFE.compareAndSwapInt(object, offset, expectValue, updateValue);
            if (b) {
                return updateValue;
            }
            return UNSAFE.getIntVolatile(object, offset);

        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        return getInt(object, paramName);
    }

}

测试对象(User)

package com.qee.unsafe;

import java.io.Serializable;

public class User implements Serializable {

    private String name;


    private int age;

    private boolean sex;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public boolean isSex() {
        return sex;
    }

    public void setSex(boolean sex) {
        this.sex = sex;
    }
}

Main类:

package com.qee.unsafe;

public class UnsafeTest {


    public static void main(String[] args) throws NoSuchFieldException {
        User user = new User();
        GASUtils.setInt(user, "age", 10);
        System.out.println("GASUtils.getInt :" + GASUtils.getInt(user, "age"));
        System.out.println("user.getAge(): " + user.getAge());

        GASUtils.setObject(user, "name", "candy");
        System.out.println("GASUtils.getObject :" + GASUtils.getObject(user, "name"));
        System.out.println("user.getName(): " + user.getName());

        int age1 = GASUtils.compareAndSetInt(user, "age", 11, 11);

        int age2 = GASUtils.compareAndSetInt(user, "age", 10, 11);
        System.out.println("age1: " + age1);
        System.out.println("age2: " + age2);
    }
}

结果:

  

 

posted @ 2018-07-12 20:23  wsMrLin  阅读(485)  评论(0编辑  收藏  举报