Object類原始碼解析

2021-09-20 03:55:25 字數 3940 閱讀 8982

object類是乙個特殊的類,是所有類的父類,如果乙個類沒有用extends明確指出繼承於某個類,那麼它預設繼承object類。這裡總結object類中的幾個方法:wait(),notify(),notifyall(),wait(long timeout), hashcode()和equals()。

1.wait()

public final void wait() throws interruptedexception
wait()方法就是使執行緒停止執行。

方法wait()的作用是使當前執行**的執行緒進行等待,該方法是用來將當前執行緒置入「預執行佇列」中,並且在wait()所在的**處停止執行,直到接到通知或被中斷為止。

wait()方法只能在同步方法中或同步塊中呼叫。如果呼叫wait()時,沒有持有適當的鎖,會丟擲異常。

wait()方法執行後,當前執行緒釋放鎖,執行緒與其它執行緒競爭重新獲取鎖。

觀察一下wait()方法的使用:

public static void main(string args) throws interruptedexception 

system.out.println("main方法結束...");

}

這樣在執行到object.wait()之後就一直等待下去,那麼程式肯定不能一直這麼等待下去了。這個時候就需要使用到了另外乙個方法喚醒的方法notify()。

2.notify()

public final native void notify();
notify方法就是使停止的執行緒繼續執行。

方法notify()也要在同步方法或同步塊中呼叫,該方法是用來通知那些可能等待該物件的物件鎖的其它執行緒,對其發出通知notify,並使它們重新獲取該物件的物件鎖。如果有多個執行緒等待,則有執行緒規劃器隨機挑選出乙個呈wait狀態的執行緒。

在notify()方法後,當前執行緒不會馬上釋放該物件鎖,要等到執行notify()方法的執行緒將程式執行完,也就是退出同步**塊之後才會釋放物件鎖。

注意:wait,notify必須使用在synchronized同步方法或者**塊內。

3.notifyall()

public final native void notifyall();
notify方法只是喚醒某乙個等待執行緒,notifyall方法可以一次喚醒所有的等待執行緒.

4.wait(long timeout)

public final native void wait(long timeout) throws interruptedexception;
wait(long timeout) 方法的作用和wait()類似,但是增加了乙個超時的設定,如果等待時間超過了timeout設定的毫秒數,那麼當前執行緒會繼續執行

總結:

wait() 方法用來控制當前執行緒停止執行,等待其他執行緒對此object例項呼叫notify或者notifyall方法之後再繼續執行 .

wait(long timeout) 此方法的作用和wait()類似,但是增加了乙個超時的設定,如果等待時間超過了timeout設定的毫秒數,那麼當前執行緒會繼續執行

notify()方法從所有wait執行緒中選擇乙個執行緒,讓它開始執行

notifyall()方法通知所有等待此物件的執行緒,開始執行

如果執行緒呼叫了物件的 wait()方法,那麼執行緒便會處於該物件的等待池中,等待池中的執行緒不會去競爭該物件的鎖。

當有執行緒呼叫了物件的 notifyall()方法(喚醒所有 wait 執行緒)或 notify()方法(只隨機喚醒乙個 wait 執行緒),被喚醒的的執行緒便會進入該物件的鎖池中,鎖池中的執行緒會去競爭該物件鎖。

優先順序高的執行緒競爭到物件鎖的概率大,假若某執行緒沒有競爭到該物件鎖,它還會留在鎖池中,唯有執行緒再次呼叫wait()方法,它才會重新回到等待池中。而競爭到物件鎖的執行緒則繼續往下執行,直到執行完了 synchronized **塊,它會釋放掉該物件鎖,這時鎖池中的執行緒會繼續競爭該物件鎖。

5.hashcode()

public native int hashcode();
hashcode的存在主要是為了查詢的快捷性,hashcode是用來在雜湊儲存結構中確定物件的儲存位址的(後半句說的用hashcode來代表物件就是在hash表中的位置)

為什麼hashcode就查詢的更快,比如:我們有乙個能存放1000個數這樣大的記憶體中,在其中要存放1000個不一樣的數字,用最笨的方法,就是存乙個數字,就遍歷一遍,看有沒有相同得數,當存了900個數字,開始存901個數字的時候,就需要跟900個數字進行對比,這樣就很麻煩,很是消耗時間,用hashcode來記錄物件的位置,來看一下。hash表中有1、2、3、4、5、6、7、8個位置,存第乙個數,hashcode為1,該數就放在hash表中1的位置,存到100個數字,hash表中8個位置會有很多數字了,1中可能有20個數字,存101個數字時,它先查hashcode值對應的位置,假設為1,那麼就有20個數字和他的hashcode相同,它只需要跟這20個數字相比較(equals),如果每乙個相同,那麼就放在1這個位置,這樣比較的次數就少了很多,實際上hash表中有很多位置,這裡只是舉例只有8個,所以比較的次數會讓你覺得也挺多的,實際上,如果hash表很大,那麼比較的次數就很少很少了。

6.equals()**

public boolean equals(object obj)
很明顯,該方法就是用來判斷兩個物件是否是同乙個物件. 其底層是使用了「==」來實現,也就是說通過比較兩個物件的記憶體位址是否相同判斷是否是同乙個物件。

equals()和hashcode()有什麼關係呢?

舉個栗子: 有n個桶(桶相當於hash表中劃分的區域),每個桶中存放了很多物件,物件1通過hash計算放到了其中乙個桶中,那麼當物件2通過hash計算也放到這個桶中時,怎麼識別桶中物件是否和它一樣呢?

1.equals()相等的兩個物件他們的hashcode()肯定相等,也就是用equals()對比是絕對可靠的。

2.hashcode()相等的兩個物件他們的equals()不一定相等,也就是hashcode()不是絕對可靠的。

所有對於需要大量並且快速的對比的話如果都用equals()去做顯然效率太低,所以解決方式是,每當需要對比的時候,首先用hashcode()去對比,如果hashcode()不一樣,則表示這兩個物件肯定不相等(也就是不必再用equals()去再對比了),如果hashcode()相同,此時再對比他們的equals(),如果equals()也相同,則表示這兩個物件是真的相同了,這樣既能大大提高了效率也保證了對比的絕對正確性!

hashcode是用於雜湊資料的快速訪問,如利用hashset/hashmap/hashtable類來儲存資料時,都是根據儲存物件的hashcode值來進行判斷是否相同的. 這樣如果我們對乙個物件重寫了euqals,意思是只要物件的成員變數值都相等那麼euqals就等於true,但不重寫hashcode,那麼我們再new乙個新的物件,當原物件.equals(新物件)等於true時,兩者的hashcode卻是不一樣的,由此將產生了理解的不一致,如在儲存雜湊集合時(如set類),將會儲存了兩個值一樣的物件,導致混淆,因此,就也需要重寫hashcode()

(換個理解)如果僅僅重寫了equals(),而沒有重寫hashcode()方法,會出現什麼情況?

字段屬性值完全相同的兩個物件因為hashcode不同,所以在hashmap中的table陣列的下標不同,從而這兩個物件就會同時存在於集合中.(當然用不到雜湊表的話,其實僅僅重寫equals()方法也可以,官方建議重寫equals(),一定要重寫hashcode()).

Object 原始碼分析

equals 方法實際判斷引用位址 public boolean equals object obj 這就是用hashcode 方法判斷的。hashcode 是object類的方法 equals 方法也是 所有類都間接或直接繼承了object類,因此都繼承了hashcode 方法。object類的h...

LinkedHashSet類原始碼解析

linkedhashset概述 linkedhashset是具有可預知迭代順序的set介面的雜湊表和鏈結列表實現。此實現與hashset的不同之處在於,後者維護著乙個執行於所有條目的雙重鏈結列表。此鏈結列表定義了迭代順序,該迭代順序可為插入順序或是訪問順序。注意,此實現不是同步的。如果多個執行緒同時...

LinkedList實現類原始碼解析

增 刪 改 查 tostring linkedlist並不是通過陣列實現的,而是一種雙向鍊錶的結構來實現。這種結構的特點為 查詢效率低,增刪效率高,執行緒不安全 所有,增加或刪除元素較多時,我們選用linkedlist。linkedlist是通過雙向鍊錶實現的,首先,我們得了解雙向鍊錶。雙向鍊錶的結...