Java如何以及为什么使用Unsafe?
總覽
sun.misc.Unsafe至少在Java 1.4(2004)中就已經(jīng)存在于Java中。 在Java 9中,不安全將與許多其他供內(nèi)部使用的類一起隱藏。 以提高JVM的可維護性。 盡管仍不確定究竟將取代Unsafe到底是什么,但我懷疑將取代Unsafe不僅僅是一件事,但它提出了一個問題,為什么要使用它?
執(zhí)行Java語言不允許的但仍然有用的操作。
Java不允許使用低級語言可用的許多技巧。 對于大多數(shù)開發(fā)人員而言,這是一件好事,不僅可以使您免于自己,而且還可以使您從同事中免除。 這也使導入開放源代碼更加容易,因為您知道它們可以造成的損害是有限的。 或至少可以限制您意外執(zhí)行的操作。 如果您盡力而為,仍然可能造成傷害。
但是您為什么還要嘗試,您可能會感到奇怪? 在不安全的情況下構(gòu)建庫時,很多(但不是全部)方法很有用,并且在某些情況下,如果不使用JNI,則沒有其他方法可以做同樣的事情,這更加危險,并且您將失去“一次編譯,在任何地方運行”的權(quán)限。 ”
對象反序列化
使用框架反序列化或構(gòu)建對象時,您要假設(shè)要重新構(gòu)成以前存在的對象。 您期望您將使用反射來調(diào)用類的setter,或者更可能直接設(shè)置內(nèi)部字段,甚至最終字段。 問題是您想創(chuàng)建一個對象的實例,但是您實際上并不需要構(gòu)造函數(shù),因為這可能只會使事情變得更加困難并產(chǎn)生副作用。
public class A implements Serializable {private final int num;public A(int num) {System.out.println("Hello Mum");this.num = num;}public int getNum() {return num;} }在此類中,您應該能夠重建和設(shè)置final字段,但是如果您必須調(diào)用構(gòu)造函數(shù),并且它可能完成與反序列化無關(guān)的事情。 由于這些原因,許多庫使用Unsafe來創(chuàng)建實例而不調(diào)用構(gòu)造函數(shù)。
Unsafe unsafe = getUnsafe(); Class aClass = A.class; A a = (A) unsafe.allocateInstance(aClass);不需要時,調(diào)用allocateInstance可以避免調(diào)用適當?shù)臉?gòu)造函數(shù)。
線程安全訪問直接內(nèi)存
Unsafe的另一個用途是對堆外內(nèi)存的線程安全訪問。 ByteBuffer使您可以安全地訪問堆外或直接內(nèi)存,但是它沒有任何線程安全操作。 如果要在進程之間共享數(shù)據(jù),這特別有用。
import sun.misc.Unsafe; import sun.nio.ch.DirectBuffer;import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.reflect.Field; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel;public class PingPongMapMain {public static void main(String... args) throws IOException {boolean odd;switch (args.length < 1 ? "usage" : args[0].toLowerCase()) {case "odd":odd = true;break;case "even":odd = false;break;default:System.err.println("Usage: java PingPongMain [odd|even]");return; }int runs = 10000000;long start = 0;System.out.println("Waiting for the other odd/even");File counters = new File(System.getProperty("java.io.tmpdir"), "counters.deleteme"); counters.deleteOnExit();try (FileChannel fc = new RandomAccessFile(counters, "rw").getChannel()) {MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024);long address = ((DirectBuffer) mbb).address();for (int i = -1; i < runs; i++) {for (; ; ) {long value = UNSAFE.getLongVolatile(null, address);boolean isOdd = (value & 1) != 0;if (isOdd != odd)// wait for the other side.continue;// make the change atomic, just in case there is more than one odd/even processif (UNSAFE.compareAndSwapLong(null, address, value, value + 1))break;}if (i == 0) {System.out.println("Started");start = System.nanoTime();}}}System.out.printf("... Finished, average ping/pong took %,d ns%n",(System.nanoTime() - start) / runs);}static final Unsafe UNSAFE;static {try {Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);UNSAFE = (Unsafe) theUnsafe.get(null);} catch (Exception e) {throw new AssertionError(e);}} }當您在兩個程序中運行此程序時,一個程序帶有奇數(shù) ,另一個程序帶有偶數(shù) 。 您可以看到每個進程都通過持久共享內(nèi)存來更改數(shù)據(jù)。
在每個程序中,它會將相同的磁盤高速緩存映射到進程中。 內(nèi)存中實際上只有一個副本。 這意味著可以共享內(nèi)存,前提是您使用線程安全操作,例如volatile和CAS操作。
i7-3970X的輸出為
等待另一個奇/偶
已開始
…完成后,平均ping / pong花費了83 ns
這是兩個進程之間的83 ns往返時間。 當您考慮使用System V IPC大約需要2500 ns,并且IPC易變而不是持續(xù)存在時,這很快。
使用不安全適合工作嗎?
我不建議您直接使用不安全。 它比自然的Java開發(fā)需要更多的測試。 因此,我建議您使用已經(jīng)過使用測試的庫。 如果您想自己使用Unsafe,建議您在獨立的庫中測試它的用法。 這限制了在應用程序中使用“不安全”的方式,并為您提供了一個更安全,不安全的方式。
結(jié)論
有趣的是Java中存在Unsafe,您可能想在家中玩它。 它有一些工作應用程序,尤其是在編寫低級庫時,但是總的來說,最好使用經(jīng)過測試的Unsafe庫,而不是直接使用它。
翻譯自: https://www.javacodegeeks.com/2014/12/how-and-why-is-unsafe-used-in-java.html
總結(jié)
以上是生活随笔為你收集整理的Java如何以及为什么使用Unsafe?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 三急是指哪三急 三急分别是什么
- 下一篇: 速冻手抓饼做法 速冻手抓饼如何做