最近中文字幕高清中文字幕无,亚洲欧美高清一区二区三区,一本色道无码道dvd在线观看 ,一个人看的www免费高清中文字幕

Unsafe 類的使用

1. 前言

本節(jié)對(duì) Unsafe 類的使用進(jìn)行講解,上一小節(jié)內(nèi)容已經(jīng)對(duì) Unsafe 類的常用方法有了大體的概括,本節(jié)主要內(nèi)容點(diǎn)如下:
Unsafe 類的簡(jiǎn)介,對(duì) UnSafe 類有一個(gè)整體的認(rèn)識(shí);
Unsafe 類的創(chuàng)建以及創(chuàng)建過程中避免的異常機(jī)制,這是開始使用 UnSafe 類的前提;
了解 Unsafe 類操作對(duì)象屬性方法的使用,這是本節(jié)內(nèi)容的重點(diǎn)之一;
了解 Unsafe 操作數(shù)組元素方法的使用,也是本節(jié)內(nèi)容的重點(diǎn)之一。

本節(jié)內(nèi)容意在了解并掌握 Unsafe 類的常用方法的使用。

2. Unsafe 類簡(jiǎn)介

Unsafe 類是 Java 整個(gè)并發(fā)包底層實(shí)現(xiàn)的核心,它具有像 C++ 的指針一樣直接操作內(nèi)存的能力,而這也就意味著其越過了 JVM 的限制。

Unsafe 類有如下的特點(diǎn):

  • Unsafe 不受 JVM 管理,也就無法被自動(dòng) GC,需要手動(dòng) GC,容易出現(xiàn)內(nèi)存泄漏;
  • Unsafe 的大部分方法中必須提供原始地址 (內(nèi)存地址) 和被替換對(duì)象的地址,偏移量需自行計(jì)算,一旦出現(xiàn)問題必然是 JVM 崩潰級(jí)別的異常,會(huì)導(dǎo)致整個(gè)應(yīng)用程序直接 crash;
  • 直接操作內(nèi)存,也意味著其速度更快,在高并發(fā)的條件之下能夠很好地提高效率。

3. Unsafe 類的創(chuàng)建

Unsafe 類是不可以通過 new 關(guān)鍵字直接創(chuàng)建的。Unsafe 類的構(gòu)造函數(shù)是私有的,而對(duì)外提供的靜態(tài)方法 Unsafe.getUnsafe () 又對(duì)調(diào)用者的 ClassLoader 有限制 ,如果這個(gè)方法的調(diào)用者不是由 Boot ClassLoader 加載的,則會(huì)報(bào)錯(cuò)。

實(shí)例:通過 main 方法進(jìn)行調(diào)用,報(bào)錯(cuò)。

import sun.misc.Unsafe;
import sun.misc.VM;
import sun.reflect.Reflection;

public class DemoTest {
    public static void main(String[] args) {
        getUnsafe();
    }
    public static Unsafe getUnsafe() {
        Class unsafeClass = Reflection.getCallerClass();
        if (!VM.isSystemDomainLoader(unsafeClass.getClassLoader())) {
            throw new SecurityException("Unsafe");
        } else {
            return null;
        }
    }
}

運(yùn)行結(jié)果

Exception in thread "main" java.lang.InternalError: CallerSensitive annotation expected at frame 1
	at sun.reflect.Reflection.getCallerClass(Native Method)
	at leeCode.DemoTest.getUnsafe(DemoTest.java:12)
	at leeCode.DemoTest.main(DemoTest.java:9)

報(bào)錯(cuò)原因: Java 源碼中由開發(fā)者自定義的類都是由 Appliaction ClassLoader 加載的,也就是說 main 函數(shù)所依賴的 jar 包都是 ClassLoader 加載的,所以會(huì)報(bào)錯(cuò)。

所以正常情況下我們無法直接使用 Unsafe ,如果需要使用它,則需要利用反射

實(shí)例:通過反射,成功加載 Unsafe 類。

import sun.misc.Unsafe;
import java.lang.reflect.Field;

public class DemoTest {
    public static void main(String[] args) {
        Unsafe unsafe = getUnsafe();
        System.out.println("Unsafe 加載成功:"+unsafe);
    }
    public static Unsafe getUnsafe() {
        Unsafe unsafe = null;
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return unsafe;
    }
}

結(jié)果驗(yàn)證

Unsafe 加載成功:sun.misc.Unsafe@677327b6

總結(jié):Unsafe 類的加載必須使用反射進(jìn)行,否則會(huì)報(bào)錯(cuò)。

4. Unsafe 類操作對(duì)象屬性

操作對(duì)象屬性的常用方法有

  • public native Object getObject(Object o, long offset):獲取一個(gè) Java 對(duì)象中偏移地址為 offset 的屬性的值,此方法可以突破修飾符的限制,類似的方法有 getInt ()、getDouble () 等,同理還有 putObject () 方法;
  • public native Object getObjectVolatile(Object o, long offset):強(qiáng)制從主存中獲取目標(biāo)對(duì)象指定偏移量的屬性值,類似的方法有 getIntVolatile (),getDoubleVolatile () 等,同理還有 putObjectVolatile ();
  • public native void putOrderedObject(Object o, long offset, Object x):設(shè)置目標(biāo)對(duì)象中偏移地址 offset 對(duì)應(yīng)的對(duì)象類型屬性的值為指定值。這是一個(gè)有序或者有延遲的 putObjectVolatile () 方法,并且不保證值的改變被其他線程立即看到。只有在屬性被 volatile 修飾并且期望被修改的時(shí)候使用才會(huì)生效,類似的方法有 putOrderedInt () 和 putOrderedLong ();
  • public native long objectFieldOffset(Field f):返回給定的非靜態(tài)屬性在它的類的存儲(chǔ)分配中的位置 (偏移地址),然后可根據(jù)偏移地址直接對(duì)屬性進(jìn)行修改,可突破屬性的訪問修飾符限制。

實(shí)例

import sun.misc.Unsafe;
import java.lang.reflect.Field;

public class DemoTest {
    private String name;
    public static void main(String[] args) {
        Unsafe unsafe = getUnsafe();
        try {
            DemoTest directMemory = (DemoTest) unsafe.allocateInstance(DemoTest.class);
            //獲取name屬性
            long nameOffset = unsafe.objectFieldOffset(DemoTest.class.getDeclaredField("name"));
            //設(shè)置name屬性
            unsafe.putObject(directMemory, nameOffset, "并發(fā)編程");
            System.out.println("屬性設(shè)置成功:"+ directMemory.getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static Unsafe getUnsafe() {
        Unsafe unsafe = null;
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return unsafe;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

結(jié)果驗(yàn)證:

屬性設(shè)置成功:并發(fā)編程

5. Unsafe 操作數(shù)組元素

Unsafe 操作數(shù)組元素主要有如下兩個(gè)方法:

  • public native int arrayBaseOffset(Class arrayClass):返回?cái)?shù)組類型的第一個(gè)元素的偏移地址 (基礎(chǔ)偏移地址);
  • public native int arrayIndexScale(Class arrayClass):返回?cái)?shù)組中元素與元素之間的偏移地址的增量,配合 arrayBaseOffset () 使用就可以定位到任何一個(gè)元素的地址。

實(shí)例

import sun.misc.Unsafe;
import java.lang.reflect.Field;

public class DemoTest {
    private static String[] names = {"多線程", "Java", "并發(fā)編程"};
    public static void main(String[] args) {
        Unsafe unsafe = getUnsafe();
        try {
            Class<?> a = String[].class;
            int base = unsafe.arrayBaseOffset(a);
            int scale = unsafe.arrayIndexScale(a);
            // base + i * scale 即為字符串?dāng)?shù)組下標(biāo) i 在對(duì)象的內(nèi)存中的偏移地址
            System.out.println(unsafe.getObject(names, (long) base + 2 * scale));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static Unsafe getUnsafe() {
        Unsafe unsafe = null;
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return unsafe;
    }
}

結(jié)果驗(yàn)證

并發(fā)編程

通過對(duì)數(shù)組的元素的地址進(jìn)行內(nèi)存偏移,最后得到的結(jié)果為最后一個(gè)元素,并發(fā)編程。base + 2 * scale 表示字符串?dāng)?shù)組下標(biāo) i 在對(duì)象的內(nèi)存中的偏移地址,偏移兩個(gè)元素,得到最后一個(gè)元素。

6. 小結(jié)

本節(jié)內(nèi)容主要對(duì) Unsafe 類的常用方法的使用進(jìn)行介紹,使學(xué)習(xí)者能夠在使用 Unsafe 類操作對(duì)象和數(shù)組時(shí),能夠快速的使用課程中提供的實(shí)例思路。其實(shí) Unsafe 類還可以操作內(nèi)存地址,操作 CAS,對(duì)于初學(xué)者來說就比較晦澀了。

此處對(duì)操作對(duì)象和操作數(shù)組常用的方法的講解,是本節(jié)的核心知識(shí),掌握 Unsafe 類的常用方法的使用,非常重要。