1. 程式人生 > >Java 併發隨身記(一)之 Unsafe類

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);
    }
}

結果:

相關推薦

Java 併發隨身 Unsafe

  最近在看Java併發相關的內容,需要自己整理整理,不然就生疏了。工作2年多,工作時一般注都是框架、訊息這些內容,對基礎內容比較忽視。閒話不說,既然是併發內容,首先先複習一下Unsafe的內容吧。 Unsafe 類提供了硬體級別的原子操作,它提供非常有趣的一些內容。首先我們看下Unsafe檔案。並對其一些內

java筆記IO流位元組流

 1.概念     * IO流用來處理裝置之間的資料傳輸     * Java對資料的操作是通過流的方式     * Java用於操作流的類都在IO包中     

Java併發程式設計系列避免死鎖

避免死鎖 (1)避免一個執行緒同時獲取多個鎖 (2)避免一個執行緒在鎖內佔用多個資源,儘量保證每個鎖只佔用一個資源 (3)使用定時鎖,使用lock.trylock(timeout)替代內部鎖機制 (4)

Java併發——鎖框架

來源:《Java執行緒與併發程式設計實踐》以及          https://blog.csdn.net/qq_38293564/article/details/80476659 1. 鎖框架 java.util.co

Java併發——Executor框架

對看過的資料進行了整理,方便自己學習 來源:https://www.cnblogs.com/love-Stefanie/p/6728228.html            https://www.cnblogs.com/

超詳細的Java面試題總結Java基礎知識篇

福利:看本文之前,推薦給大家一個阿里雲雙11活動,真的非常非常非常推薦,對於新人福利,阿里雲這次真的是下血本了,建議阿里雲新人一定一定一定不要錯過。如果覺得這單純是廣告的話(阿里雲肯找我做廣告就好了,嘿嘿),你可以直接跳過看正文。 阿里雲雙11最新活動(僅限阿

淺談Java併發程式設計系列—— 如何保證執行緒安全

執行緒安全類 保證類執行緒安全的措施: 不共享執行緒間的變數; 設定屬性變數為不可變變數; 每個共享的可變變數都使用一個確定的鎖保護; 保證執行緒安全的思路: 通過架構設計 通過

Java進階 ——— Java多執行緒程序和執行緒

引言 講到執行緒,不可避免的提到程序。而因為執行緒無法脫離程序單獨存在,那什麼是程序? 延伸閱讀,Java多執行緒系列文章 什麼是程序? 程序:具有一定獨立功能的程式關於某個資料集合上的一次執行活動,程序是系統進行資源分配和排程的最小單位。 例如手機執行的眾多

Java 內存監控 jps命令

alt jps命令 dea 技術分享 命令 輸入 內存監控 沒有 options 今天看一下Java命令行工具 jps的使用 一、命令簡介   jps [ options ] [ hostid ]   不輸入 [ hostid ] 內容,則默認是本機。 二、options

Java併發程式設計學習——標準Thread

1、雖然System.out.println內部是加了鎖的,但是如果System.out.println(i- -),依然是執行緒不安全的,因為有的JVM,i- -需要三步才能完成。 2、通過interrupt方法停止執行緒 public class Int

Opencv學習Mat

作為初學者,我對於Opencv的學習總是比較迷茫,還好可以借鑑別人的部落格。Mat類的內容比較多,這次我也只是對它有個基礎、淺層的認識,在以後的學習和應用中我會根據需要和例項再不斷補充學習。一 背景    在Opencv1.X時代,資料型別為 IpIImage;    在Op

java 併發程式設計學習筆記 併發基礎

                                              併發基礎 併發小測試 java.util.concurrent.Semaphore 類 public class SemTest { /** * Se

java 併發程式設計學習筆記 基礎框架搭建和併發模擬工具,程式碼

                                基礎框架搭建和併發模擬工具,程式碼 (1)基礎框架搭建 (2)併發模擬 (3)CountDownLatch  通常用來 保證 幾個執行緒執行完成之後,再執行其他的程式碼 Semaphore

java學習——java基礎概念解析

userinfo shuf cdn pdm shu href ember sig lower 鵲拙崩系06凳q毫乙6http://docstore.docin.com/sina_6341933819 6j50uk佬詼4wn刮掖http://shequ.docin.com/

從零一起學Spring BootLayIM項目長成 初見 Spring Boot

部分 基礎 依賴 com stat boot.s 情況下 比較 tar 項目背景   之前寫過LayIM的.NET版後端實現,後來又寫過一版Java的。當時用的是servlet,websocket和jdbc。雖然時間過去很久了,但是仍有些同學在關註。偶然間我聽說了Sprin

數據結構 - 從二叉搜索樹說到AVL樹二叉搜索樹的操作與詳解Java

判斷 right 不為 exist avl 輸入 位置 bubuko get   二叉搜索樹(Binary Search Tree),簡稱BST,顧名思義,一顆可以用於搜索的二叉樹。BST在數據結構中占有很重要的地位,一些高級樹結構都是其的變種,例如AVL樹、紅黑樹等,因此

Java設計模式建立型模式:工廠模式簡單工廠模式+工廠方法模式

在面向物件程式設計中, 最通常的方法是一個new操作符產生一個物件例項,new操作符就是用來構造物件例項的。但是在一些情況下, new操作符直接生成物件會帶來一些問題。舉例來說,許多型別物件的建立需要一系列的步驟:你可能需要計算或取得物件的初始位置;選擇生成哪個子物件例項;或在你生成你需要的物件

Java前世今生

一、Java語言是什麼? 一種計算機程式語言,名字取自咖啡。 二、Java語言發展簡史 Java語言之父 : James Gosling SUN (Stanford University Network 斯坦福大學網路公司) 1995年5月23日 Java

Java面向物件與多執行緒綜合實驗封裝、繼承與多型

編寫一個程式,實現檔案管理系統中的使用者管理模組。要求模組中實現使用者的模擬登入過程。通過使用者輸入,獲取使用者名稱和口令;與事先記錄在程式中的使用者資訊進行對比,通過口令驗證後才能使用系統。使用者分為系統管理人員、檔案錄入人員,檔案瀏覽人員三類,相關類圖如下所示。 (1)要求在使用者類中

Java併發程式設計和高併發學習總結-大綱

系列 開篇語 想寫這樣一個東西很久了,在慕課網上學完某老師的課程(避免打廣告的嫌疑就不貼出來了,感興趣的同學可以去慕課網上去搜來看看,是個付費課程)之後就覺得應該有這樣的一個學習總結的東西來,後來因為懶又有其他事情耽誤了,然後又上了新專案(正好拿來練手了,當然