亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java Unsafe學(xué)習(xí)筆記分享

 更新時間:2021年11月12日 12:13:38   作者:憂傷的比目魚  
今天小編就為大家分享一篇Java Unsafe學(xué)習(xí)筆記,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

sun.misc.Unsafe

作用:可以用來在任意內(nèi)存地址位置處讀寫數(shù)據(jù),支持一些CAS原子操作

Java最初被設(shè)計為一種安全的受控環(huán)境。盡管如此,HotSpot還是包含了一個后門sun.misc.Unsafe,提供了一些可以直接操控內(nèi)存和線程的底層操作。Unsafe被JDK廣泛應(yīng)用于java.nio和并發(fā)包等實現(xiàn)中,這個不安全的類提供了一個觀察HotSpot JVM內(nèi)部結(jié)構(gòu)并且可以對其進行修改,但是不建議在生產(chǎn)環(huán)境中使用

獲取Unsafe實例

Unsafe對象不能直接通過new Unsafe()或調(diào)用Unsafe.getUnsafe()獲取,原因如下:

  • 不能直接new Unsafe(),原因是Unsafe被設(shè)計成單例模式,構(gòu)造方法是私有的;
  • 不能通過調(diào)用Unsafe.getUnsafe()獲取,因為getUnsafe被設(shè)計成只能從引導(dǎo)類加載器(bootstrap class loader)加載
@CallerSensitive
public static Unsafe getUnsafe() {
    Class var0 = Reflection.getCallerClass();
    if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
        throw new SecurityException("Unsafe");
    } else {
        return theUnsafe;
    }
}

獲取實例

//方法一:我們可以令我們的代碼“受信任”。運行程序時,使用bootclasspath選項,指定系統(tǒng)類路徑加上你使用的一個Unsafe路徑
java -Xbootclasspath:/usr/jdk1.7.0/jre/lib/rt.jar:. com.mishadoff.magic.UnsafeClient
// 方法二
static {
    try {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        UNSAFE = (Unsafe) field.get(null);
    } catch (Exception e) {
    }
}

注意:忽略你的IDE。比如:eclipse顯示”Access restriction…”錯誤,但如果你運行代碼,它將正常運行。如果這個錯誤提示令人煩惱,可以通過以下設(shè)置來避免:

Preferences -> Java -> Compiler -> Errors/Warnings -> Deprecated and restricted API -> Forbidden reference -> Warning

重點API

  • allocateInstance(Class<?> var1)不調(diào)用構(gòu)造方法生成對象
User instance = (User) UNSAFE.allocateInstance(User.class);
  • objectFieldOffset(Field var1)返回成員屬性在內(nèi)存中的地址相對于對象內(nèi)存地址的偏移量
  • putLong,putInt,putDouble,putChar,putObject等方法,直接修改內(nèi)存數(shù)據(jù)(可以越過訪問權(quán)限)
package com.quancheng;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class CollectionApp {
    private static sun.misc.Unsafe UNSAFE;
    public static void main(String[] args) {
        try {
            User instance = (User) UNSAFE.allocateInstance(User.class);
            instance.setName("luoyoub");
            System.err.println("instance:" + instance);
            instance.test();
            Field name = instance.getClass().getDeclaredField("name");
            UNSAFE.putObject(instance, UNSAFE.objectFieldOffset(name), "huanghui");
            instance.test();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    static {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            UNSAFE = (Unsafe) field.get(null);
        } catch (Exception e) {
        }
    }
}
class User {
    private String name;
    public void setName(String name) {
        this.name = name;
    }
    public void test() {
        System.err.println("hello,world" + name);
    }
}
  • copyMemory:內(nèi)存數(shù)據(jù)拷貝
  • freeMemory:用于釋放allocateMemory和reallocateMemory申請的內(nèi)存
  • compareAndSwapInt/compareAndSwapLongCAS操作
  • getLongVolatile/putLongVolatile

使用場景

避免初始化

當(dāng)你想要跳過對象初始化階段,或繞過構(gòu)造器的安全檢查,或?qū)嵗粋€沒有任何公共構(gòu)造器的類,allocateInstance方法是非常有用的,使用構(gòu)造器、反射和unsafe初始化它,將得到不同的結(jié)果

public class CollectionApp {
    private static sun.misc.Unsafe UNSAFE;
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        A a = new A();
        a.test(); // output ==> 1
        A a1 = A.class.newInstance();
        a1.test(); // output ==> 1
        A instance = (A) UNSAFE.allocateInstance(A.class);
        instance.test(); // output ==> 0
    }
    static {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            UNSAFE = (Unsafe) field.get(null);
        } catch (Exception e) {
        }
    }
}
class A{
    private long a;
    public A(){
        a = 1;
    }
    public void test(){
        System.err.println("a==>" + a);
    }
}

內(nèi)存崩潰(Memory corruption)

Unsafe可用于繞過安全的常用技術(shù),直接修改內(nèi)存變量;實際上,反射可以實現(xiàn)相同的功能。但值得關(guān)注的是,我們可以修改任何對象,甚至沒有這些對象的引用

Guard guard = new Guard();
guard.giveAccess();   // false, no access
// bypass
Unsafe unsafe = getUnsafe();
Field f = guard.getClass().getDeclaredField("ACCESS_ALLOWED");
unsafe.putInt(guard, unsafe.objectFieldOffset(f), 42); // memory corruption
guard.giveAccess(); // true, access granted

注意:我們不必持有這個對象的引用

  • 淺拷貝(Shallow copy)
  • 動態(tài)類(Dynamic classes)

我們可以在運行時創(chuàng)建一個類,比如從已編譯的.class文件中。將類內(nèi)容讀取為字節(jié)數(shù)組,并正確地傳遞給defineClass方法;當(dāng)你必須動態(tài)創(chuàng)建類,而現(xiàn)有代碼中有一些代理, 這是很有用的

private static byte[] getClassContent() throws Exception {
    File f = new File("/home/mishadoff/tmp/A.class");
    FileInputStream input = new FileInputStream(f);
    byte[] content = new byte[(int)f.length()];
    input.read(content);
    input.close();
    return content;
}
byte[] classContents = getClassContent();
Class c = getUnsafe().defineClass(
              null, classContents, 0, classContents.length);
c.getMethod("a").invoke(c.newInstance(), null); // 1

拋出異常(Throw an Exception)

該方法拋出受檢異常,但你的代碼不必捕捉或重新拋出它,正如運行時異常一樣

getUnsafe().throwException(new IOException());

大數(shù)組(Big Arrays)

正如你所知,Java數(shù)組大小的最大值為Integer.MAX_VALUE。使用直接內(nèi)存分配,我們創(chuàng)建的數(shù)組大小受限于堆大??;實際上,這是堆外內(nèi)存(off-heap memory)技術(shù),在java.nio包中部分可用;

這種方式的內(nèi)存分配不在堆上,且不受GC管理,所以必須小心Unsafe.freeMemory()的使用。它也不執(zhí)行任何邊界檢查,所以任何非法訪問可能會導(dǎo)致JVM崩潰

class SuperArray {
    private final static int BYTE = 1;
    private long size;
    private long address;
    public SuperArray(long size) {
        this.size = size;
        address = getUnsafe().allocateMemory(size * BYTE);
    }
    public void set(long i, byte value) {
        getUnsafe().putByte(address + i * BYTE, value);
    }
    public int get(long idx) {
        return getUnsafe().getByte(address + idx * BYTE);
    }
    public long size() {
        return size;
    }
}
long SUPER_SIZE = (long)Integer.MAX_VALUE * 2;
SuperArray array = new SuperArray(SUPER_SIZE);
System.out.println("Array size:" + array.size()); // 4294967294
for (int i = 0; i < 100; i++) {
    array.set((long)Integer.MAX_VALUE + i, (byte)3);
    sum += array.get((long)Integer.MAX_VALUE + i);
}
System.out.println("Sum of 100 elements:" + sum);  // 300

并發(fā)(Concurrency)

幾句關(guān)于Unsafe的并發(fā)性。compareAndSwap方法是原子的,并且可用來實現(xiàn)高性能的、無鎖的數(shù)據(jù)結(jié)構(gòu)

掛起與恢復(fù)

定義:

public native void unpark(Thread jthread);  
public native void park(boolean isAbsolute, long time); // isAbsolute參數(shù)是指明時間是絕對的,還是相對的

將一個線程進行掛起是通過park方法實現(xiàn)的,調(diào)用park后,線程將一直阻塞直到超時或者中斷等條件出現(xiàn)。unpark可以終止一個掛起的線程,使其恢復(fù)正常。整個并發(fā)框架中對線程的掛起操作被封裝在 LockSupport類中,LockSupport類中有各種版本pack方法,但最終都調(diào)用了Unsafe.park()方法;

unpark函數(shù)為線程提供“許可(permit)”,線程調(diào)用park函數(shù)則等待“許可”。這個有點像信號量,但是這個“許可”是不能疊加的,“許可”是一次性的;比如線程B連續(xù)調(diào)用了三次unpark函數(shù),當(dāng)線程A調(diào)用park函數(shù)就使用掉這個“許可”,如果線程A再次調(diào)用park,則進入等待狀態(tài),見下例Example1

Example1:
// 針對當(dāng)前線程已經(jīng)調(diào)用過unpark(多次調(diào)用unpark的效果和調(diào)用一次unpark的效果一樣)
public static void main(String[] args) throws InterruptedException {
    Thread currThread = Thread.currentThread();
    UNSAFE.unpark(currThread);
    UNSAFE.unpark(currThread);
    UNSAFE.unpark(currThread);
    UNSAFE.park(false, 0);
    UNSAFE.park(false, 0);
    System.out.println("SUCCESS!!!");
}
// 恢復(fù)線程interrupt() && UNSAFE.unpark()運行結(jié)果一樣
public static void main(String[] args) throws InterruptedException {
    Thread currThread = Thread.currentThread();
    new Thread(()->{
        try {
            Thread.sleep(3000);
            System.err.println("sub thread end");
            // currThread.interrupt();
            UNSAFE.unpark(currThread);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }).start();
    UNSAFE.park(false, 0);
    System.out.println("SUCCESS!!!");
}
// 如果是相對時間也就是isAbsolute為false(注意這里后面的單位納秒)到期的時候,與Thread.sleep效果相同,具體有什么區(qū)別有待深入研究
//相對時間后面的參數(shù)單位是納秒
UNSAFE.park(false, 3000000000l);
System.out.println("SUCCESS!!!");
long time = System.currentTimeMillis()+3000;
//絕對時間后面的參數(shù)單位是毫秒
UNSAFE.park(true, time);
System.out.println("SUCCESS!!!");

注意,unpark函數(shù)可以先于park調(diào)用。比如線程B調(diào)用unpark函數(shù),給線程A發(fā)了一個“許可”,那么當(dāng)線程A調(diào)用park時,它發(fā)現(xiàn)已經(jīng)有“許可”了,那么它會馬上再繼續(xù)運行。實際上,park函數(shù)即使沒有“許可”,有時也會無理由地返回,實際上在SUN Jdk中,object.wait()也有可能被假喚醒;

注意:unpark方法最好不要在調(diào)用park前對當(dāng)前線程調(diào)用unpark

Unsafe API

sun.misc.Unsafe類包含105個方法。實際上,對各種實體操作有幾組重要方法,其中的一些如下:
Info.僅返回一些低級的內(nèi)存信息
addressSize
pageSize
Objects.提供用于操作對象及其字段的方法
allocateInstance     ##直接獲取對象實例
objectFieldOffset
Classes.提供用于操作類及其靜態(tài)字段的方法
staticFieldOffset
defineClass
defineAnonymousClass
ensureClassInitialized
Arrays.操作數(shù)組
arrayBaseOffset
arrayIndexScale
Synchronization.低級的同步原語
monitorEnter
tryMonitorEnter
monitorExit
compareAndSwapInt
putOrderedInt
Memory.直接內(nèi)存訪問方法
allocateMemory
copyMemory
freeMemory
getAddress
getInt
putInt

知識點

Unsafe.park()當(dāng)遇到線程終止時,會直接返回(不同于Thread.sleep,Thread.sleep遇到thread.interrupt()會拋異常)

// Thread.sleep會拋異常
public static void main(String[] args) throws InterruptedException {
   Thread thread =  new Thread(()->{
        try {
            System.err.println("sub thread start");
            Thread.sleep(10000);
            System.err.println("sub thread end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
   thread.start();
    TimeUnit.SECONDS.sleep(3);
    thread.interrupt();
    System.out.println("SUCCESS!!!");
}
output==>
sub thread start
SUCCESS!!!
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at com.quancheng.ConcurrentTest.lambda$main$0(ConcurrentTest.java:13)
    at java.lang.Thread.run(Thread.java:745)
Process finished with exit code 0
public static void main(String[] args) throws InterruptedException {
   Thread thread =  new Thread(()->{
       System.err.println("sub thread start");
       UNSAFE.park(false,0);
       System.err.println("sub thread end");
    });
   thread.start();
    TimeUnit.SECONDS.sleep(3);
    UNSAFE.unpark(thread);
    System.out.println("SUCCESS!!!");
}
output==>
sub thread start
sub thread end
SUCCESS!!!
Process finished with exit code 0

unpark無法恢復(fù)處于sleep中的線程,只能與park配對使用,因為unpark發(fā)放的許可只有park能監(jiān)聽到

public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(() -> {
        try {
            System.err.println("sub thread start");
            TimeUnit.SECONDS.sleep(10);
            System.err.println("sub thread end");
        } catch (Exception e) {
            e.printStackTrace();
        }
    });
    thread.start();
    TimeUnit.SECONDS.sleep(3);
    UNSAFE.unpark(thread);
    System.out.println("SUCCESS!!!");
}

park和unpark的靈活之處

上面已經(jīng)提到,unpark函數(shù)可以先于park調(diào)用,這個正是它們的靈活之處。

一個線程它有可能在別的線程unPark之前,或者之后,或者同時調(diào)用了park,那么因為park的特性,它可以不用擔(dān)心自己的park的時序問題,否則,如果park必須要在unpark之前,那么給編程帶來很大的麻煩!!

”考慮一下,兩個線程同步,要如何處理?

在Java5里是用wait/notify/notifyAll來同步的。wait/notify機制有個很蛋疼的地方是,比如線程B要用notify通知線程A,那么線程B要確保線程A已經(jīng)在wait調(diào)用上等待了,否則線程A可能永遠都在等待。編程的時候就會很蛋疼。

另外,是調(diào)用notify,還是notifyAll?

notify只會喚醒一個線程,如果錯誤地有兩個線程在同一個對象上wait等待,那么又悲劇了。為了安全起見,貌似只能調(diào)用notifyAll了“

park/unpark模型真正解耦了線程之間的同步,線程之間不再需要一個Object或者其它變量來存儲狀態(tài),不再需要關(guān)心對方的狀態(tài)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java躲不過設(shè)計模式的坑之代理模式詳解

    Java躲不過設(shè)計模式的坑之代理模式詳解

    設(shè)計模式看來更像是一種設(shè)計思維或設(shè)計思想,為你的項目工程提供方向,讓你的項目工程更加健壯、靈活,延續(xù)生命力。本文即將分享的是設(shè)計模式的其中一種:代理模式,感興趣的可以了解一下
    2022-09-09
  • Mybatis如何通過接口實現(xiàn)sql執(zhí)行原理解析

    Mybatis如何通過接口實現(xiàn)sql執(zhí)行原理解析

    為了簡化MyBatis的使用,MyBatis提供了接口方式自動化生成調(diào)用過程,可以大大簡化MyBatis的開發(fā),下面這篇文章主要給大家介紹了關(guān)于Mybatis如何通過接口實現(xiàn)sql執(zhí)行原理解析的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • Java中Elasticsearch 實現(xiàn)分頁方式(三種方式)

    Java中Elasticsearch 實現(xiàn)分頁方式(三種方式)

    Elasticsearch是用Java語言開發(fā)的,并作為Apache許可條款下的開放源碼發(fā)布,是一種流行的企業(yè)級搜索引擎,這篇文章主要介紹了Elasticsearch實現(xiàn)分頁的3種方式,需要的朋友可以參考下
    2022-07-07
  • Java訪問修飾符原理及代碼解析

    Java訪問修飾符原理及代碼解析

    這篇文章主要介紹了Java訪問修飾符原理及代碼解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-06-06
  • Java容器ArrayList原理解析

    Java容器ArrayList原理解析

    這篇文章主要介紹了Java容器ArrayList原理解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-02-02
  • springboot不同環(huán)境使用不同配置文件打包方式

    springboot不同環(huán)境使用不同配置文件打包方式

    這篇文章主要介紹了springboot不同環(huán)境使用不同配置文件打包方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • Mybatis?Plus?逆向工程介紹

    Mybatis?Plus?逆向工程介紹

    這篇文章主要介紹了Mybatis?Plus?逆向工程,文章通過Mybatis?Plus?逆向工程相關(guān)資料及示例代碼詳細介紹,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考一下
    2022-01-01
  • Java中構(gòu)造器內(nèi)部的多態(tài)方法的行為實例分析

    Java中構(gòu)造器內(nèi)部的多態(tài)方法的行為實例分析

    這篇文章主要介紹了Java中構(gòu)造器內(nèi)部的多態(tài)方法的行為,結(jié)合實例形式分析了java構(gòu)造器內(nèi)部多態(tài)方法相關(guān)原理、功能及操作技巧,需要的朋友可以參考下
    2019-10-10
  • Seata?AT模式TM處理流程圖文示例詳解

    Seata?AT模式TM處理流程圖文示例詳解

    這篇文章主要為大家介紹了Seata?AT模式TM處理流程圖文示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • java把字符串寫入文件里的簡單方法分享

    java把字符串寫入文件里的簡單方法分享

    這篇文章主要介紹了java把字符串寫入到文件里的簡單方法,這是跟一個外國朋友學(xué)的代碼,需要的朋友可以參考下
    2014-03-03

最新評論