摘要:作為條件變量的的不僅可以認(rèn)為內(nèi)嵌了一把鎖,還內(nèi)嵌了一個(gè)條件變量。操作條件變量的函數(shù)將當(dāng)前線程在條件變量上阻塞,一般是為了等待其他線程的某件事情執(zhí)行完成。其它裝箱類其它裝箱類的代碼這里就不分析了。重點(diǎn)關(guān)注下各裝箱類的緩存范圍。
jdk源碼讀到現(xiàn)在這里,重要的集合類也讀了一部分了。
集合類再往下讀的話,就要涉及到兩個(gè)方向。
第一,是比較典型的但是不常用的數(shù)據(jù)結(jié)構(gòu),這部分我準(zhǔn)備將數(shù)據(jù)結(jié)構(gòu)復(fù)習(xí)、回顧后再繼續(xù)閱讀。
第二,是并發(fā)相關(guān)的集合,這部分我準(zhǔn)備留到和并發(fā)相關(guān)的類一起閱讀。
所以,今天就讀些輕松的。
Object 作為單根繼承的Objectjava的對(duì)象系統(tǒng)設(shè)計(jì)是采用單根繼承,所有的對(duì)象往上追溯,Object都是它們共同的祖先。
有了這個(gè)假設(shè),我忽然想起java中一個(gè)有趣的事實(shí):
Listlist = new ArrayList (); list.toString();
這段代碼能正常編譯、運(yùn)行嗎?經(jīng)驗(yàn)告訴我,當(dāng)然可以。
可是從類型系統(tǒng)的角度仔細(xì)思考,list引用的類型為List
然而,List接口中并沒有toString方法,為什么能調(diào)用?
這是由于,在java中,會(huì)讓接口類型也擁有Object的所有方法。一個(gè)接口對(duì)象,也是一個(gè)Object對(duì)象。因?yàn)閱胃^承這一總體設(shè)計(jì),所以這樣設(shè)計(jì)接口是合理的。
這里有關(guān)于該問題的有趣討論,所以這里就不詳細(xì)展開了。
在java中,除了最基本的單根繼承的祖先類之外,Object還內(nèi)置了很多機(jī)制。如:
Object o = new Object(); synchronized(o) { /* ... */ }
在其它語言中,鎖這一機(jī)制都是標(biāo)準(zhǔn)庫中提供的函數(shù),成對(duì)使用。一個(gè)lock函數(shù)用于獲取鎖,一個(gè)release函數(shù)函數(shù)用于釋放鎖。
然而,java直接將鎖機(jī)制作為語法的一部分,還給它一個(gè)專屬關(guān)鍵字synchronized。每個(gè)Object對(duì)象,都內(nèi)嵌了一個(gè)鎖。java稱之監(jiān)視器鎖。
這樣設(shè)計(jì)有什么好處呢?一種觀點(diǎn)是,將鎖機(jī)制內(nèi)置為語法的一部分,有利于jvm對(duì)其進(jìn)行深度優(yōu)化提升性能,如java的鎖升級(jí)機(jī)制。
作為條件變量的Objectjava的Object不僅可以認(rèn)為內(nèi)嵌了一把鎖,還內(nèi)嵌了一個(gè)條件變量。操作條件變量的函數(shù):
public final native void notify(); public final native void notifyAll(); public final native void wait(long timeout) throws InterruptedException;
wait將當(dāng)前線程在條件變量上阻塞,一般是為了等待其他線程的某件事情執(zhí)行完成。當(dāng)其他線程的事情執(zhí)行完成后,在條件變量上調(diào)用notify或notifyAll來喚醒阻塞的線程。
可以看到,這三個(gè)方法都是native,jvm原生實(shí)現(xiàn)。
wait還有兩個(gè)重載形式:
public final void wait() throws InterruptedException { wait(0); } public final void wait(long timeout, int nanos) throws InterruptedException { if (timeout < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos > 0) { timeout++; } wait(timeout); }
比較有意思的是第二個(gè)。
原生實(shí)現(xiàn)的wait(long timeout),只能設(shè)置毫秒級(jí)別的超時(shí)時(shí)間。但是這個(gè)wait(long timeout, int nanos)卻能設(shè)置納秒級(jí)別的超時(shí)時(shí)間。怎么實(shí)現(xiàn)的?
if (nanos > 0) { timeout++; }
笑哭了。。。。難道是我下載的jdk平臺(tái)不對(duì)?
hashCode、equals、toStringObject類提供了這三個(gè)函數(shù)的默認(rèn)實(shí)現(xiàn)。來看一下:
public native int hashCode(); public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } public boolean equals(Object obj) { return (this == obj); }
可以看到,hashCode的默認(rèn)方法是原生實(shí)現(xiàn),到底是不是指針不清楚。
equals方法的默認(rèn)實(shí)現(xiàn)僅僅簡單比較了是否為同一引用。
toString()方法打印出的是類名及十六進(jìn)制的hash值。
裝箱拆箱裝箱拆箱機(jī)制的存在的原因是:
java中的泛型是類型擦除,類似集合等泛型類中實(shí)際存放的必須是Object的子類,也即引用類型。
java的8種基本類型都是值類型,不是對(duì)象。因此無法直接放入泛型類對(duì)象中。
為了解決這個(gè)沖突,只好設(shè)計(jì)一組對(duì)象,中間包裹基本類型,并且語法層次內(nèi)建裝箱類與基本類型的自動(dòng)轉(zhuǎn)換機(jī)制,也即自動(dòng)裝箱拆箱。
下面以Integer為例分析裝箱拆箱類的源碼。
Integer大致看一下Integer中的組成。可以發(fā)現(xiàn)有三個(gè)不同的部分:
Integer類本身作為裝箱容器。
Integer類的static屬性定義了大量和int有關(guān)的常量。
Integer類的static方法定義了和int有關(guān)的工具函數(shù)。
屬性和構(gòu)造函數(shù)先來看屬性。
private final int value; public Integer(int value) { this.value = value; }
對(duì)的,Integer對(duì)象中,只有包含這么一個(gè)數(shù)據(jù),被裝箱的原始值。
簡單到不能再簡單。
我們知道,一般來說,在java中,使用工廠方法代替構(gòu)造函數(shù)是更好的設(shè)計(jì)。在Integer里,就體現(xiàn)了它的好處之一。
Integer提供了一組靜態(tài)工廠方法:
public static Integer valueOf(String s) throws NumberFormatException { return Integer.valueOf(parseInt(s, 10)); } public static Integer valueOf(String s, int radix) throws NumberFormatException { return Integer.valueOf(parseInt(s,radix)); } public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
前兩個(gè)工廠方法都利用最后一個(gè)工廠方法實(shí)現(xiàn)。最重要的是最后一個(gè)。
非常明顯,當(dāng)被裝箱的原始類型i在IntegerCache.low和IntegerCache.high之間時(shí),則返回緩存的Integer對(duì)象。
來看IntegerCache:
private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[]; static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; } private IntegerCache() {} }
可發(fā)現(xiàn):
默認(rèn)緩存的值是-128到127。
緩存的范圍可以通過java.lang.Integer.IntegerCache.high來設(shè)置。這樣,如果在某些場景下Integer影響性能,可以通過jvm手動(dòng)修改該參數(shù)空間換時(shí)間。
總結(jié)一下,由于Integer是對(duì)象,而對(duì)整數(shù)的操作是代碼里非常頻繁的地方。裝箱機(jī)制會(huì)導(dǎo)致程序產(chǎn)生大量的Integer對(duì)象,這導(dǎo)致:
對(duì)象會(huì)占據(jù)額外空間(如對(duì)象頭),造成內(nèi)存浪費(fèi)。
頻繁創(chuàng)建銷毀對(duì)象,給gc造成壓力。
因此,采用緩存機(jī)制,盡量降低裝箱對(duì)性能的影響。
其它裝箱類其它裝箱類的代碼這里就不分析了。重點(diǎn)關(guān)注下各裝箱類的緩存范圍。
首先,Boolean,只有兩個(gè)值,當(dāng)然可以都緩存。
浮點(diǎn)類型,Double和Float,沒有緩存:
public static Float valueOf(float f) { return new Float(f); } public static Double valueOf(double d) { return new Double(d); }
Short,緩存范圍為-128到127,和默認(rèn)的Integer一樣。最重要的是,這個(gè)范圍無法修改。
public static Short valueOf(short s) { final int offset = 128; int sAsInt = s; if (sAsInt >= -128 && sAsInt <= 127) { // must cache return ShortCache.cache[sAsInt + offset]; } return new Short(s); } private static class ShortCache { private ShortCache(){} static final Short cache[] = new Short[-(-128) + 127 + 1]; static { for(int i = 0; i < cache.length; i++) cache[i] = new Short((short)(i - 128)); } }
同樣:
Byte緩存范圍也是-128到127,全部緩存。
而Character緩存范圍為0到127.
Long的緩存范圍為-128到127。
可以發(fā)現(xiàn),只有Integer的緩存范圍能夠修改,其它的裝箱類型都不行。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/77093.html
摘要:本章部分內(nèi)容從源碼中解讀一些自動(dòng)裝箱與拆箱的原理,以及會(huì)出現(xiàn)的一些陷阱已經(jīng)性能等。例題分析我們通過幾個(gè)經(jīng)典的問題,來看看大家到底理解了裝箱與拆箱的知識(shí)點(diǎn)沒。 showImg(https://img-blog.csdnimg.cn/20190426221838971.gif);showImg(https://img-blog.csdnimg.cn/20190426221918208.pn...
摘要:棧隊(duì)列雙端隊(duì)列都是非常經(jīng)典的數(shù)據(jù)結(jié)構(gòu)。結(jié)合了棧和隊(duì)列的特點(diǎn)。因此,在中,有棧的使用需求時(shí),使用代替。迭代器之前源碼源碼之與字段中分析過,容器的實(shí)現(xiàn)中,所有修改過容器結(jié)構(gòu)的操作都需要修改字段。 棧、隊(duì)列、雙端隊(duì)列都是非常經(jīng)典的數(shù)據(jù)結(jié)構(gòu)。和鏈表、數(shù)組不同,這三種數(shù)據(jù)結(jié)構(gòu)的抽象層次更高。它只描述了數(shù)據(jù)結(jié)構(gòu)有哪些行為,而并不關(guān)心數(shù)據(jù)結(jié)構(gòu)內(nèi)部用何種思路、方式去組織。本篇博文重點(diǎn)關(guān)注這三種數(shù)據(jù)結(jié)構(gòu)...
摘要:哪吒社區(qū)技能樹打卡打卡貼函數(shù)式接口簡介領(lǐng)域優(yōu)質(zhì)創(chuàng)作者哪吒公眾號(hào)作者架構(gòu)師奮斗者掃描主頁左側(cè)二維碼,加入群聊,一起學(xué)習(xí)一起進(jìn)步歡迎點(diǎn)贊收藏留言前情提要無意間聽到領(lǐng)導(dǎo)們的談話,現(xiàn)在公司的現(xiàn)狀是碼農(nóng)太多,但能獨(dú)立帶隊(duì)的人太少,簡而言之,不缺干 ? 哪吒社區(qū)Java技能樹打卡?【打卡貼 day2...
摘要:不相等的對(duì)象要具有不相等的哈希碼為了哈希表的操作效率,這一點(diǎn)很重要,但不是強(qiáng)制要求,最低要求是不相等的對(duì)象不能共用一個(gè)哈希碼。方法和方法協(xié)同工作,返回對(duì)象的哈希碼。這個(gè)哈希碼基于對(duì)象的身份生成,而不是對(duì)象的相等性。 本文面向 剛學(xué)完Java的新手們。這篇文章不講語法,而是一些除了語法必須了解的概念。 將要去面試的初級(jí)工程師們。查漏補(bǔ)缺,以免遭遇不測。 目前由于篇幅而被挪出本文的知識(shí)...
摘要:最近算是比較深入的了解了一下的源碼,就想著寫點(diǎn)東西記錄一下,一來可以加深理解,再來也算是為我刷了那么久平臺(tái)貢獻(xiàn)一點(diǎn)自己的綿薄之力。這兩個(gè)方法都是給當(dāng)前的實(shí)例的屬性賦值,參數(shù)為類型的構(gòu)造器直接將參數(shù)賦值給屬性,參數(shù)為是將方法的返回值賦值。 最近算是比較深入的了解了一下Integer的源碼,就想著寫點(diǎn)東西記錄一下,一來可以加深理解,再來也算是為我刷了那么久segmentfault平臺(tái)貢獻(xiàn)一...
閱讀 2810·2019-08-30 15:55
閱讀 2857·2019-08-30 15:53
閱讀 2294·2019-08-26 13:47
閱讀 2557·2019-08-26 13:43
閱讀 3154·2019-08-26 13:33
閱讀 2802·2019-08-26 11:53
閱讀 1796·2019-08-23 18:35
閱讀 798·2019-08-23 17:16