成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Java跨平臺(tái)?慎用這些有平臺(tái)差異性的方法

hidogs / 1184人閱讀

摘要:坑一慎用方法在類中,有一個(gè)方法是,返回的是一個(gè)數(shù)組,該數(shù)組包含了所包含的方法。坑二慎用線程優(yōu)先級(jí)做并發(fā)處理線程中有屬性,表示線程的優(yōu)先級(jí),默認(rèn)值為,取值區(qū)間為。顯然,運(yùn)行時(shí)環(huán)境是因操作系統(tǒng)而異的。

本文為作者原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處。

我們都知道Java是跨平臺(tái)的,一次編譯,到處運(yùn)行,本質(zhì)上依賴于不同操作系統(tǒng)下有不同的JVM。到處運(yùn)行是做到了,但運(yùn)行結(jié)果呢?一樣的程序,在不同的JVM上跑的結(jié)果是否一樣呢?很遺憾,程序的執(zhí)行結(jié)果沒(méi)有百分百的確定性,本篇分享我遇到的一些case。

坑一 慎用Class.getMethods()方法

在Class類中,有一個(gè)方法是getMethods(),返回的是一個(gè)Method數(shù)組,該數(shù)組包含了Class所包含的方法。但是需要注意的是,其數(shù)組元素的排序是不確定的,在不同的機(jī)器上會(huì)有不一樣的排序輸出。

public Method[] getMethods() throws SecurityException {
    checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
    return copyMethods(privateGetPublicMethods());
}

阿里的fastjson就曾經(jīng)在這里踩到坑了,fastjson是序列化框架,當(dāng)要去獲取對(duì)象的某個(gè)屬性值時(shí),往往需要通過(guò)反射調(diào)用getter方法。比如,有個(gè)屬性field,那么通過(guò)遍歷Method數(shù)組,判斷是否有g(shù)etField方法,如果有的話,則調(diào)用取得相應(yīng)的值。

但對(duì)于boolean類型的字段,其getter方法有可能是isXXX,也有可能是getXXX,而fastjson在遍歷時(shí),只要判斷有isXXX或者getXXX,就認(rèn)定其為getter方法,然后立即執(zhí)行該getter方法。

// 偽代碼
for (Method method : someObject.class.getMethods()) {
    // 判斷是否為getter方法
    if(method.getName().equals("getField") || method.getName().equals("isField")){
        // 通過(guò)getter取得屬性值
        return method.invoke(xxx, xxxx);
    }
}

但是如果一個(gè)對(duì)象同時(shí)存在isA和getA方法呢?

private A a;

private boolan isA(){
    return false;
}

private A getA(){
    return a;
}

這個(gè)時(shí)候fastjson到底執(zhí)行的是isA()還是getA()呢?答案是不確定,因?yàn)閕sA和getA在返回的Method數(shù)組中順序是不確定的,所以有的機(jī)器上可能是通過(guò)isA()來(lái)獲取屬性值,有的機(jī)器上可能是通過(guò)getA()來(lái)獲取屬性值,而這兩個(gè)方法返回的一個(gè)是boolean類型,一個(gè)是A類型,導(dǎo)致fastjson在不同機(jī)器執(zhí)行的結(jié)果是不一樣的。

為什么這個(gè)方法返回值不按照字母排序呢?每個(gè)類或者方法名字都會(huì)對(duì)應(yīng)一個(gè)Symbol對(duì)象,在這個(gè)名字第一次使用的時(shí)候構(gòu)建,Symbol對(duì)象是通過(guò)malloc來(lái)分配的,因此新分配的Symbol對(duì)象的地址就不一定比后分配的Symbol對(duì)象地址小,也不一定大,因?yàn)槠陂g存在內(nèi)存free的動(dòng)作,那地址是不會(huì)一直線性變化的,之所以不按照字母排序,主要還是為了速度考慮,根據(jù)Symbol對(duì)象的地址排序是最快的。

坑二 慎用線程優(yōu)先級(jí)做并發(fā)處理

線程Thread中有priority屬性,表示線程的優(yōu)先級(jí),默認(rèn)值為5,取值區(qū)間為[1,10]。雖然在Thread的注釋中有說(shuō)明優(yōu)先級(jí)高的線程將會(huì)被優(yōu)先執(zhí)行,但是測(cè)試結(jié)果,卻是隨機(jī)的。

如下,

static class Runner implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"---"+i);
        }
    }
}

public static void main(String[] args) {

    Thread t1 = new Thread(new Runner(), "thread-1");
    Thread t2 = new Thread(new Runner(), "thread-2");
    Thread t3 = new Thread(new Runner(), "thread-3");

    t1.setPriority(10); // t1 線程優(yōu)先級(jí)設(shè)置為10
    t2.setPriority(5);  // t2 線程優(yōu)先級(jí)設(shè)置為5
    t3.setPriority(1);  // t3 線程優(yōu)先級(jí)設(shè)置為1

    t1.start();
    t2.start();
    t3.start();
}

如果是嚴(yán)格按照線程優(yōu)先級(jí)來(lái)執(zhí)行的,那么應(yīng)該是t1執(zhí)行for循環(huán),然后t2執(zhí)行完for循環(huán),最后t3執(zhí)行for循環(huán)。但實(shí)際上測(cè)試結(jié)果顯示,每次執(zhí)行的輸出順序都沒(méi)有遵循這個(gè)規(guī)則,并且每次執(zhí)行的結(jié)果都是不一樣的。

---- console output ----
thread-2---0
thread-2---1
thread-3---0
thread-1---0
thread-1---1
thread-1---2
thread-3---1
......
......

線程調(diào)度具有很多不確定性,線程的優(yōu)先級(jí)只是對(duì)線程的一個(gè)標(biāo)志,但不代表著這是絕對(duì)的優(yōu)先,具體的執(zhí)行順序都是由操作系統(tǒng)本身的資源調(diào)度來(lái)決定的。不同操作系統(tǒng)本身的線程調(diào)度方式可能存在差異性,所以不能依靠線程優(yōu)先級(jí)來(lái)處理并發(fā)邏輯。

坑三 慎用系統(tǒng)時(shí)間做精確時(shí)間計(jì)算

Java API中,一般使用native方法System.currentTimeMillis() 來(lái)獲取系統(tǒng)的時(shí)間。從方法名上,可以看出,該方法用于獲取系統(tǒng)當(dāng)前的時(shí)間,即從1970年1月1日8時(shí)到當(dāng)前的毫秒值。

下面羅列出了官方對(duì)該方法的注釋:

public final class System {
    /**
     * Note that while the unit of time of the return value is a millisecond,
     * the granularity of the value depends on the underlying
     * operating system and may be larger.  For example, many
     * operating systems measure time in units of tens of
     * milliseconds.
     */
    public static native long currentTimeMillis();
}

方法注釋明確指出了這個(gè)毫秒值的精度在不同的操作系統(tǒng)中是存在差異的,有的系統(tǒng)1毫秒實(shí)際上等同于物理時(shí)間的幾十毫秒。也就是說(shuō),在一個(gè)性能測(cè)試中,因?yàn)榫炔灰恢碌膯?wèn)題,有的系統(tǒng)得出的結(jié)果是1毫秒,另外系統(tǒng)得出的性能結(jié)果卻是10毫秒。

那如何實(shí)現(xiàn)高精度的時(shí)間計(jì)算呢?先來(lái)看看System.nanoTime()方法,下面列出了官方的核心注釋:

public final class System {
    
    /**
    * This method can only be used to measure elapsed time and is
    * not related to any other notion of system or wall-clock time.
    */
    public static native long nanoTime();
}

這個(gè)方法只能用于檢測(cè)系統(tǒng)經(jīng)過(guò)的時(shí)間,也就是說(shuō)其返回的時(shí)間不是從1970年1月1日8時(shí)開(kāi)始的納秒時(shí)間,是從系統(tǒng)啟動(dòng)開(kāi)始時(shí)開(kāi)始計(jì)算的時(shí)間。

所以一般高精度的時(shí)間是采用System.nanoTime()方法來(lái)實(shí)現(xiàn)的,其單位為納秒(十億分之一秒),雖然不保證完全準(zhǔn)確的納秒級(jí)精度。但用該方法來(lái)實(shí)現(xiàn)毫秒級(jí)精度的計(jì)算,是綽綽有余的,如下。

 long start = System.nanoTime();
 // do something
 long end = System.nanoTime();
 
 // 程序執(zhí)行的時(shí)間,精確到毫秒
 long costTime = (end - start) / 1000000L
坑四 慎用運(yùn)行時(shí)Runtime類

Runtime是JVM中運(yùn)行時(shí)環(huán)境的抽象,包含了運(yùn)行時(shí)環(huán)境的一些信息,每個(gè)Java應(yīng)用程序都有一個(gè)Runtime實(shí)例,用于應(yīng)用程序和其所在的運(yùn)行時(shí)環(huán)境進(jìn)行交互。應(yīng)用程序本身無(wú)法創(chuàng)建Runtime實(shí)例,只能通過(guò)Runtime.getRuntime()方法來(lái)獲取。

顯然,運(yùn)行時(shí)環(huán)境是因操作系統(tǒng)而異的。其交互方式也存在差異,
例如,

// Windows下調(diào)用程序
Process proc =Runtime.getRuntime().exec("exefile");
// Linux下調(diào)用程序
Process proc =Runtime.getRuntime().exec("./exefile");

所以,如果應(yīng)用程序中包含這類和運(yùn)行時(shí)環(huán)境進(jìn)行交互的方法,應(yīng)確保應(yīng)用的部署環(huán)境不變,如果不能保證的話,那么至少需要提供兩套運(yùn)行時(shí)交互邏輯。

以上是我遇到的不能跨平臺(tái)的一些case,其實(shí)本質(zhì)上都和native實(shí)現(xiàn)有關(guān)。你有沒(méi)有遇到一些這樣的坑呢?歡迎留言~

參考鏈接:
JVM源碼分析之不保證順序的Class.getMethods

公眾號(hào)簡(jiǎn)介:作者是螞蟻金服的一線開(kāi)發(fā),分享自己的成長(zhǎng)和思考之路。內(nèi)容涉及數(shù)據(jù)、工程、算法。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/76221.html

相關(guān)文章

  • Effective Java 第三版 全文翻譯

    摘要:本章中的大部分內(nèi)容適用于構(gòu)造函數(shù)和方法。第項(xiàng)其他方法優(yōu)先于序列化第項(xiàng)謹(jǐn)慎地實(shí)現(xiàn)接口第項(xiàng)考慮使用自定義的序列化形式第項(xiàng)保護(hù)性地編寫(xiě)方法第項(xiàng)對(duì)于實(shí)例控制,枚舉類型優(yōu)先于第項(xiàng)考慮用序列化代理代替序列化實(shí)例附錄與第版中項(xiàng)目的對(duì)應(yīng)關(guān)系參考文獻(xiàn) effective-java-third-edition 介紹 Effective Java 第三版全文翻譯,純屬個(gè)人業(yè)余翻譯,不合理的地方,望指正,感激...

    galois 評(píng)論0 收藏0
  • Docker大坑小洼

    摘要:正在學(xué)習(xí),留著看看轉(zhuǎn)自的大坑小洼成為云計(jì)算領(lǐng)域的新寵兒已經(jīng)是不爭(zhēng)的事實(shí),作為高速發(fā)展的開(kāi)源項(xiàng)目,難免存在這樣或那樣的瑕疵。話不多說(shuō),一起來(lái)領(lǐng)略的大坑小洼。原因回歸至上文的第一個(gè)坑。如此一來(lái),只要內(nèi)部涉及到域名解析,則立即受到影響。 正在學(xué)習(xí)Docker,留著看看 轉(zhuǎn)自Docker的大坑小洼 Docker成為云計(jì)算領(lǐng)域的新寵兒已經(jīng)是不爭(zhēng)的事實(shí),作為高速發(fā)展的開(kāi)源項(xiàng)目,難免存在這樣或那樣...

    My_Oh_My 評(píng)論0 收藏0
  • Hyperledger Fabric(介紹)

    摘要:比特幣和以太幣屬于一類區(qū)塊鏈,我們將其歸類為公共無(wú)許可的區(qū)塊鏈技術(shù)。例如,在單個(gè)企業(yè)中部署時(shí),或由受信任的權(quán)威機(jī)構(gòu)運(yùn)作,完全拜占庭容錯(cuò)的共識(shí)可能被認(rèn)為是不必要的,并且對(duì)性能和吞吐量造成過(guò)度的拖累。 介紹 一般而言,區(qū)塊鏈?zhǔn)且粋€(gè)不可變的交易分類賬,維護(hù)在一個(gè)分布式對(duì)等節(jié)點(diǎn)網(wǎng)絡(luò)中。這些節(jié)點(diǎn)通過(guò)應(yīng)用已經(jīng)由共識(shí)協(xié)議驗(yàn)證的交易來(lái)維護(hù)分類帳的副本,該交易被分組為包括將每個(gè)塊綁定到前一個(gè)塊的散列的塊...

    yunhao 評(píng)論0 收藏0
  • 初次接觸java

    摘要:運(yùn)行環(huán)境解釋器開(kāi)發(fā)工具包編譯器類庫(kù)工具安裝執(zhí)行安裝包環(huán)境變量配置安裝目錄,讓第三方依賴于的軟件使用的工具命令所在目錄,已有值后拼接字節(jié)碼文件所在目錄,一般配置當(dāng)前目錄第一個(gè)程序格式類名如編譯源文件名運(yùn)行類名中的代碼都是包含在類之中。 計(jì)算機(jī)組成: 輸出設(shè)備 輸入設(shè)備 運(yùn)算器、控制器(cpu) 存儲(chǔ)器(硬盤(pán)、內(nèi)存) --馮洛伊曼體系結(jié)構(gòu) 計(jì)算機(jī)中數(shù)據(jù)處理方式:二進(jìn)制、只有加法 原碼:二...

    maxmin 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<