導航:首頁 > 以太坊區 > 以太坊java源碼分析

以太坊java源碼分析

發布時間:2023-04-11 15:55:18

『壹』 以太坊源碼分析(一 簡介)

以太坊作為目前區塊鏈技術2.0的代表作品,無論是它獨創的智能合約以及它本身交易的速度都優於bitcoin,通過看它的白皮書以及一些文章也略微了解了它的一些原理,但是總體還是對它的實現半知半解。
因此就想分析下它的實現源碼,再結合白皮書也許可以深入的理解它的實現。

每個包的作用大致為:

以上為個人初步理解,如有不當之處望指正

註:資料查詢主要位置 wiki eip

『貳』 有沒有詳解Java代碼的軟體

有一些工具可以幫助您詳解 Java 代碼,包括以下幾種類型:

『叄』 【深度知識】以太坊數據序列化RLP編碼/解碼原理

RLP(Recursive Length Prefix),中文翻譯過來叫遞歸長度前綴編碼,它是以太坊序列化所採用的編碼方式。RLP主要用於以太坊中數據的網路傳輸和持久化存儲。

對象序列化方法有很多種,常見的像JSON編碼,但是JSON有個明顯的缺點:編碼結果比較大。例如有如下的結構:

變數s序列化的結果是{"name":"icattlecoder","sex":"male"},字元串長度35,實際有效數據是icattlecoder 和male,共計16個位元組,我們可以看到JSON的序列化時引入了太多的冗餘信息。假設以太坊採用JSON來序列化,那麼本來50GB的區塊鏈可能現在就要100GB,當然實際沒這么簡單。

所以,以太坊需要設計一種結果更小的編碼方法。

RLP編碼的定義只處理兩類數據:一類是字元串(例如位元組數組),一類是列表。字元串指的是一串二進制數據,列表是一個嵌套遞歸的結構,裡面可以包含字元串和列表,例如["cat",["puppy","cow"],"horse",[[]],"pig",[""],"sheep"]就是一個復雜的列表。其他類型的數據需要轉成以上的兩類,轉換的規則不是RLP編碼定義的,可以根據自己的規則轉換,例如struct可以轉成列表,int可以轉成二進制(屬於字元串一類),以太坊中整數都以大端形式存儲。

從RLP編碼的名字可以看出它的特點:一個是遞歸,被編碼的數據是遞歸的結構,編碼演算法也是遞歸進行處理的;二是長度前綴,也就是RLP編碼都帶有一個前綴,這個前綴是跟被編碼數據的長度相關的,從下面的編碼規則中可以看出這一點。

對於值在[0, 127]之間的單個位元組,其編碼是其本身。

例1:a的編碼是97。

如果byte數組長度l <= 55,編碼的結果是數組本身,再加上128+l作為前綴。

例2:空字元串編碼是128,即128 = 128 + 0。

例3:abc編碼結果是131 97 98 99,其中131=128+len("abc"),97 98 99依次是a b c。

如果數組長度大於55, 編碼結果第一個是183加數組長度的編碼的長度,然後是數組長度的本身的編碼,最後是byte數組的編碼。

請把上面的規則多讀幾篇,特別是數組長度的編碼的長度。

例4:編碼下面這段字元串:

The length of this sentence is more than 55 bytes, I know it because I pre-designed it
這段字元串共86個位元組,而86的編碼只需要一個位元組,那就是它自己,因此,編碼的結果如下:

184 86 84 104 101 32 108 101 110 103 116 104 32 111 102 32 116 104 105 115 32 115 101 110 116 101 110 99 101 32 105 115 32 109 111 114 101 32 116 104 97 110 32 53 53 32 98 121 116 101 115 44 32 73 32 107 110 111 119 32 105 116 32 98 101 99 97 117 115 101 32 73 32 112 114 101 45 100 101 115 105 103 110 101 100 32 105 116
其中前三個位元組的計算方式如下:

184 = 183 + 1,因為數組長度86編碼後僅佔用一個位元組。
86即數組長度86
84是T的編碼
例5:編碼一個重復1024次"a"的字元串,其結果為:185 4 0 97 97 97 97 97 97 ...。
1024按 big endian編碼為004 0,省略掉前面的零,長度為2,因此185 = 183 + 2。

規則1~3定義了byte數組的編碼方案,下面介紹列表的編碼規則。在此之前,我們先定義列表長度是指子列表編碼後的長度之和。

如果列表長度小於55,編碼結果第一位是192加列表長度的編碼的長度,然後依次連接各子列表的編碼。

注意規則4本身是遞歸定義的。
例6:["abc", "def"]的編碼結果是200 131 97 98 99 131 100 101 102。
其中abc的編碼為131 97 98 99,def的編碼為131 100 101 102。兩個子字元串的編碼後總長度是8,因此編碼結果第一位計算得出:192 + 8 = 200。

如果列表長度超過55,編碼結果第一位是247加列表長度的編碼長度,然後是列表長度本身的編碼,最後依次連接各子列表的編碼。

規則5本身也是遞歸定義的,和規則3相似。

例7:

["The length of this sentence is more than 55 bytes, ", "I know it because I pre-designed it"]
的編碼結果是:

248 88 179 84 104 101 32 108 101 110 103 116 104 32 111 102 32 116 104 105 115 32 115 101 110 116 101 110 99 101 32 105 115 32 109 111 114 101 32 116 104 97 110 32 53 53 32 98 121 116 101 115 44 32 163 73 32 107 110 111 119 32 105 116 32 98 101 99 97 117 115 101 32 73 32 112 114 101 45 100 101 115 105 103 110 101 100 32 105 116
其中前兩個位元組的計算方式如下:

248 = 247 +1
88 = 86 + 2,在規則3的示例中,長度為86,而在此例中,由於有兩個子字元串,每個子字元串本身的長度的編碼各佔1位元組,因此總共佔2位元組。
第3個位元組179依據規則2得出179 = 128 + 51
第55個位元組163同樣依據規則2得出163 = 128 + 35

例8:最後我們再來看個稍復雜點的例子以加深理解遞歸長度前綴,

["abc",["The length of this sentence is more than 55 bytes, ", "I know it because I pre-designed it"]]
編碼結果是:

248 94 131 97 98 99 248 88 179 84 104 101 32 108 101 110 103 116 104 32 111 102 32 116 104 105 115 32 115 101 110 116 101 110 99 101 32 105 115 32 109 111 114 101 32 116 104 97 110 32 53 53 32 98 121 116 101 115 44 32 163 73 32 107 110 111 119 32 105 116 32 98 101 99 97 117 115 101 32 73 32 112 114 101 45 100 101 115 105 103 110 101 100 32 105 116
列表第一項字元串abc根據規則2,編碼結果為131 97 98 99,長度為4。
列表第二項也是一個列表項:

["The length of this sentence is more than 55 bytes, ", "I know it because I pre-designed it"]
根據規則5,結果為

248 88 179 84 104 101 32 108 101 110 103 116 104 32 111 102 32 116 104 105 115 32 115 101 110 116 101 110 99 101 32 105 115 32 109 111 114 101 32 116 104 97 110 32 53 53 32 98 121 116 101 115 44 32 163 73 32 107 110 111 119 32 105 116 32 98 101 99 97 117 115 101 32 73 32 112 114 101 45 100 101 115 105 103 110 101 100 32 105 116
長度為90,因此,整個列表的編碼結果第二位是90 + 4 = 94, 佔用1個位元組,第一位247 + 1 = 248

以上5條就是RPL的全部編碼規則。

各語言在具體實現RLP編碼時,首先需要將對像映射成byte數組或列表兩種形式。以go語言編碼struct為例,會將其映射為列表,例如Student這個對象處理成列表["icattlecoder","male"]

如果編碼map類型,可以採用以下列表形式:

[["",""],["",""],["",""]]

解碼時,首先根據編碼結果第一個位元組f的大小,執行以下的規則判斷:

1.如果f∈ [0,128),那麼它是一個位元組本身。

2.如果f∈[128,184),那麼它是一個長度不超過55的byte數組,數組的長度為 l=f-128

3.如果f∈[184,192),那麼它是一個長度超過55的數組,長度本身的編碼長度ll=f-183,然後從第二個位元組開始讀取長度為ll的bytes,按照BigEndian編碼成整數l,l即為數組的長度。

4.如果f∈(192,247],那麼它是一個編碼後總長度不超過55的列表,列表長度為l=f-192。遞歸使用規則1~4進行解碼。

5.如果f∈(247,256],那麼它是編碼後長度大於55的列表,其長度本身的編碼長度ll=f-247,然後從第二個位元組讀取長度為ll的bytes,按BigEndian編碼成整數l,l即為子列表長度。然後遞歸根據解碼規則進行解碼。

以上解釋了什麼叫遞歸長度前綴編碼,這個名字本身很好的解釋了編碼規則。

(1) 以太坊源碼學習—RLP編碼( https://segmentfault.com/a/1190000011763339 )
(2)簡單分析RLP編碼原理
( https://blog.csdn.net/itchosen/article/details/78183991 )

『肆』 怎麼在windows下啟動以太坊java客戶端ethereumj

以太坊源碼go-ethereum怎麼運行
安裝基於緩賣腔MIPS的linux頭文件
$ cd $PRJROOT/kernel
$ tar -xjvf linux-2.6.38.tar.bz2
$ cd linux-2.6.38

在指定路徑下創建include文件夾,用來存放相關頭文件配高。
$ mkdir -p $TARGET_PREFIX/include

保證linux源碼是干擾衫凈的。
$ make mrproper

生成需要的頭文件。
$ make ARCH=mips headers_check
$ make ARCH=mips INSTALL_HDR_PATH=dest headers_install

將dest文件夾下的所有文件復制到指定的include文件夾內。
$ cp -rv dest/include/* $TARGET_PREFIX/include

最後刪除dest文件夾
$ rm -rf dest
$ ls -l $TARGET_PREFIX/include

『伍』 揭秘Java虛擬機:JVM設計原理與實現詳細資料大全

《揭秘Java虛擬機:JVM設計原理與實現》是2017年電子工業出版社出版的圖書,作者是封亞飛。

基本介紹

內容簡介,目錄,

內容簡介

《揭秘Java虛擬機:JVM設計原理與實現》從源碼角度解讀HotSpot的內部實現機制,本書主要包含三大部分——JVM數據結構設計與實現、執行引擎機制及記憶體分配模型。數據結構部分包括Java位元組碼檔案格式、常量池解析、欄位解析、方法解析。每一部分都給出詳細的源碼實現分析,例如欄位解析一章,從源碼層面詳細分析了Java欄位重排、欄位繼承等關鍵機制。再如方法解析一章,給出了Java多態特性在源碼層面的實現方式。《揭秘Java虛擬機:JVM設計原理與實現》通過直接對原始碼的分析,從根本上梳理和澄清Java領域中的關鍵概念和機制。執行引擎部分包括Java方法調用機制、棧幀創建機制、指令集架構與解釋器實現機制。這一話題是《敗山揭秘Java虛擬機:JVM設計原理與實現》技術含量高的部分,需要讀者具備一定的匯編基礎。不過千萬不要被「匯編」這個詞給嚇著,其實在作者看來,匯編相比於高級語言而言,語法非常簡單,語義也十分清晰。執行引擎部分重點描述Java原始碼如何轉換為位元組碼,又如何從位元組碼轉換為機器指令從而能夠被物理CPU所執行的技術實現。同時詳細分析了Java函式堆疊的創建全過程,在源碼分析的過程中,帶領讀者從本質上理解到底什麼是Java函式堆疊和棧幀,以及棧幀灶此內部的詳細結構。記憶體分配部分主要包括類型創建與載入、對象實例創建與記憶體分配,例如new關鍵字的工作機制,import關鍵字的作用,再如java.lang.ClassLoader.loadClass()介面的本地實現機制。《揭秘Java虛擬機:JVM設計原理與實現》並不是簡單地分析源碼實現,而是在描述HotSpot內部實現機制的同時,分析了HotSpot如此這般實現的技術必然性。讀者在閱讀《揭秘Java虛擬機:JVM設計原理與實現》的過程中,將會在很多地方看到作者本人的這種思考。

目錄

第1章 Java虛擬機概述 1 1.1 從機器語言到Java——詹爺,你好 1 1.2 兼容的選擇:一場生產力的革命 6 1.3 中間語言翻譯 10 1.3.1 從中間語言翻譯到機器碼 11 1.3.2 通過C程式翻譯 11 1.3.3 直接翻譯為機器碼 13 1.3.4 本地編譯 16 1.4 神奇的指令 18 1.4.1 常見匯編指令 20 1.4.2 JVM指令 21 1.5 本章總結 24 第2章 Java執行引擎工作原理:方法調用 25 2.1 方法調用 26 2.1.1 真實的機器調用 26 2.1.2 C語言函式調用 41 2.2 JVM的函式調用機制 47 2.3 函式指針 53 2.4 CallStub函式指針定義 60 2.5 _call_stub_entry常式 72 2.6 本章總結 115 第3章 Java數據結構與面向對象 117 3.1 從Java演算法到數據結構 118 3.2 數據類型簡史 122 3.3 Java數據結構之偶然性 129 3.4 Java類型識別 132 3.4.1 class位元組碼概述 133 3.4.2 魔數與JVM內部的int類型 136 3.4.3 常量池與JVM內部對象模型 137 3.5 大端與小端 143 3.5.1 大端和小端的概念 146 3.5.2 大小端產生的本質原因 148 3.5.3 大小端驗證 149 3.5.4 大端和小端產生的場景 151 3.5.5 如何解決位元組序反轉 154 3.5.6 大小端問題的避免 156 3.5.7 JVM對位元組碼檔案的大小端處理 156 3.6 本章總結 159 第4章 Java位元組碼實戰 161 4.1 位元組碼格式初探 161 4.1.1 准備測試用例 162 4.1.2 使用javap命令分析位元組碼檔案 162 4.1.3 查看位元組碼二進制 165 4.2 魔數與版本 166 4.2.1 魔數 168 4.2.2 版本號 168 4.3 常量池 169 4.3.1 常量池的基本結構 169 4.3.2 JVM所定義的11種常量 170 4.3.3 常量池元素的復合結構 170 4.3.4 常量池的結束位置 172 4.3.5 常量池元素總數量 172 4.3.6 第一個常量池元素 173 4.3.7 第二個常量池元素 174 4.3.8 父類常量 174 4.3.9 變數型常量池元素 175 4.4 訪問標識與繼承信息 177 4.4.1 aess_flags 177 4.4.2 this_class 178 4.4.3 super_class 179 4.4.4 interface 179 4.5 欄位信息 180 4.5.1 fields_count 180 4.5.2 field_info fields[fields_count] 181 4.6 方法信息 185 4.6.1 methods_count 185 4.6.2 method_info methods[methods_count] 185 4.7 本章回顧 205 第5章 常量池解析 206 5.1 常量池記憶體分配 208 5.1.1 常量池記憶體分配總體鏈路 209 5.1.2 記憶體分配 215 5.1.3 初始化記憶體 223 5.2 oop-klass模型 224 5.2.1 兩模型三維度 225 5.2.2 體系總覽 227 5.2.3 oop體系 229 5.2.4 klass體系 231 5.2.5 handle體系 234 5.2.6 oop、klass、handle的相互轉換 239 5.3 常量池klass模型(1) 244 5.3.1 klassKlass實例構建總鏈路 246 5.3.2 為klassOop申請記憶體 249 5.3.3 klassOop記憶體清零 253 5.3.4 初始化mark 253 5.3.5 初始化klassOop._metadata 258 5.3.6 初始化klass 259 5.3.7 自指 260 5.4 常量池klass模型(2) 261 5.4.1 constantPoolKlass模型構建 261 5.4.2 constantPoolOop與klass 264 5.4.3 klassKlass終結符 267 5.5 常量池解析 267 5.5.1 constantPoolOop域初始化 268 5.5.2 初始化tag 269 5.5.3 解析常量池元素 271 5.6 本章總結 279 第6章 類變數解析 280 6.1 類變數解析 281 6.2 偏移量 285 6.2.1 靜態變數偏移量 285 6.2.2 非靜態變數偏移量 287 6.2.3 Java欄位記憶體分配總結 312 6.3 從源碼看欄位繼承 319 6.3.1 欄位重排與補白 319 6.3.2 private欄位可被繼承嗎 325 6.3.3 使用HSDB驗證欄位分配與繼承 329 6.3.4 引用類型變數記憶體分配 338 6.4 本章總結 342 第7章 Java棧幀 344 7.1 entry_point常式生成 345 7.2 局部變數表創建 352 7.2.1 constMethod的記憶體布局 352 7.2.2 局部變數表空間計算 356 7.2.3 初始化局部變數區 359 7.3 堆疊與棧幀 368 7.3.1 棧幀是什麼 368 7.3.2 硬體對堆疊的支持 387 7.3.3 棧幀開辟與回收 390 7.3.4 堆疊大小與多執行緒 391 7.4 JVM的棧幀 396 7.4.1 JVM棧幀與大小確定 396 7.4.2 棧幀創建 399 7.4.3 局部變數表 421 7.5 棧幀深度與slot復用 433 7.6 最大運算元棧與運算元棧復用 436 7.7 本章總結 439 第8章 類方法解析 440 8.1 方法簽名解析與校驗 445 8.2 方法屬性解析 447 8.2.1 code屬性解析 447 8.2.2 LVT&LVTT 449 8.3 創建methodOop 455 8.4 Java方法屬性復制 459 8.5 與 461 8.6 查看運行時位元組碼指令 482 8.7 vtable 489 8.7.1 多態 489 8.7.2 C++中的多態與vtable 491 8.7.3 Java中的多態實現機制 493 8.7.4 vtable與invokevirtual指令 500 8.7.5 HSDB查看運行時vtable 502 8.7.6 miranda方法 505 8.7.7 vtable特點總結 508 8.7.8 vtable機制邏輯驗證 509 8.8 本章總結 511 第9章 執行引擎 513 9.1 執行引擎概述 514 9.2 取指 516 9.2.1 指令長度 519 9.2.2 JVM的兩級取指機制 527 9.2.3 取指指令放在哪 532 9.2.4 程式計數器在哪裡 534 9.3 解碼 535 9.3.1 模板表 535 9.3.2 匯編器 540 9.3.3 匯編 549 9.4 棧頂快取 558 9.5 棧式指令集 565 9.6 運算元棧在哪裡 576 9.7 棧幀重疊 581 9.8 entry_point常式機器指令 586 9.9 執行引擎實戰 588 9.9.1 一個簡單的例子 588 9.9.2 位元組碼運行過程分析 590 9.10 位元組碼指令實現 597 9.10.1 iconst_3 598 9.10.2 istore_0 599 9.10.3 iadd 600 9.11 本章總結 601 第10章 類的生命周期 602 10.1 類的生命周期概述 602 10.2 類載入 605 10.2.1 類載入——鏡像類與靜態欄位 611 10.2.2 Java主類載入機制 617 10.2.3 類載入器的載入機制 622 10.2.4 反射載入機制 623 10.2.5 import與new指令 624 10.3 類的初始化 625 10.4 類載入器 628 10.4.1 類載入器的定義 628 10.4.2 系統類載入器與擴展類載入器創建 634 10.4.3 雙親委派機制與破壞 636 10.4.4 預載入 638 10.4.5 引導類載入 640 10.4.6 載入、連結與延遲載入 641 10.4.7 父載入器 645 10.4.8 載入器與類型轉換 648 10.5 類實例分配 649 10.5.1 棧上分配與逃逸分析 652 10.5.2 TLAB 655 10.5.3 指針碰撞與eden區分配 657 10.5.4 清零 658 10.5.5 偏向鎖 658 10.5.6 壓棧與取指 659 10.6 本章總結 661

『陸』 java並發包源碼怎麼讀

1. 各種同步控制工具的使用

1.1 ReentrantLock

ReentrantLock感覺上是synchronized的增強版,synchronized的特點是使用簡單,一切交給JVM去處理,但是功能上是比較薄弱的。在JDK1.5之前,ReentrantLock的性能要好於synchronized,由於對JVM進行了優化,現在的JDK版本中,兩者性能是不相上下的。如果是簡單的實現,不要刻意去使用ReentrantLock。

相比於synchronized,ReentrantLock在功能上更加豐富,它具有可重入、可中斷、可限時、公平鎖等特點。

首先我們通過一個例子來說明ReentrantLock最初步的用法:

package test;

import java.util.concurrent.locks.ReentrantLock;public class Test implements Runnable{ public static ReentrantLock lock = new ReentrantLock(); public static int i = 0;

@Override public void run() { for (int j = 0; j < 10000000; j++)
{ lock.lock(); try
{
i++;
} finally
{ lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}

}

有兩個線程都對i進行++操作,為了保證線程安全,使用了ReentrantLock,從用法上可以看出,與synchronized相比,ReentrantLock就稍微復雜一點。因為必須在finally中進行解鎖操作,如果不在finally解鎖,有可能代碼出現異常鎖沒被釋放,而synchronized是由JVM來釋放鎖。

那麼ReentrantLock到底有哪些優秀的特點呢?

1.1.1 可重入

單線程可以重復進入,但要重復退出

lock.lock();
lock.lock();try{
i++;

}
finally{
lock.unlock();
lock.unlock();
}

由於ReentrantLock是重入鎖,所以可以反復得到相同的一把鎖,它有一個與鎖相關的獲取計數器,如果擁有鎖的某個線程再次得到鎖,那麼獲取計數器就加1,然後鎖需要被釋放兩次才能獲得真正釋放(重入鎖)。這模仿了synchronized肆裂的語義;如果線程進入由線程已經擁有的監控器保護的 synchronized 塊,就允許線程繼續進行,當線程退出裂叢閉第二個(或者後續)鄭基synchronized塊的時候,不釋放鎖,只有線程退出它進入的監控器保護的第一個synchronized塊時,才釋放鎖。

public class Child extends Father implements Runnable{ final static Child child = new Child();//為了保證鎖唯一
public static void main(String[] args) { for (int i = 0; i < 50; i++) { new Thread(child).start();
}
}
public synchronized void doSomething() {
System.out.println("1child.doSomething()");
doAnotherThing(); // 調用自己類中其他的synchronized方法
}
private synchronized void doAnotherThing() { super.doSomething(); // 調用父類的synchronized方法
System.out.println("3child.doAnotherThing()");
}
@Override
public void run() {
child.doSomething();
}
}class Father { public synchronized void doSomething() {
System.out.println("2father.doSomething()");
}
}

我們可以看到一個線程進入不同的synchronized方法,是不會釋放之前得到的鎖的。所以輸出還是順序輸出。所以synchronized也是重入鎖

輸出:

1child.doSomething()
2father.doSomething()
3child.doAnotherThing()
1child.doSomething()
2father.doSomething()
3child.doAnotherThing()
1child.doSomething()
2father.doSomething()
3child.doAnotherThing()
...

1.1.2.可中斷

與synchronized不同的是,ReentrantLock對中斷是有響應的。中斷相關知識查看[高並發Java 二] 多線程基礎

普通的lock.lock()是不能響應中斷的,lock.lockInterruptibly()能夠響應中斷。

我們模擬出一個死鎖現場,然後用中斷來處理死鎖

package test;import java.lang.management.ManagementFactory;import java.lang.management.ThreadInfo;import java.lang.management.ThreadMXBean;import java.util.concurrent.locks.ReentrantLock;public class Test implements Runnable{ public static ReentrantLock lock1 = new ReentrantLock(); public static ReentrantLock lock2 = new ReentrantLock(); int lock; public Test(int lock)
{ this.lock = lock;
} @Override
public void run()
{ try
{ if (lock == 1)
{
lock1.lockInterruptibly(); try
{
Thread.sleep(500);
} catch (Exception e)
{ // TODO: handle exception
}
lock2.lockInterruptibly();
} else
{
lock2.lockInterruptibly(); try
{
Thread.sleep(500);
} catch (Exception e)
{ // TODO: handle exception
}
lock1.lockInterruptibly();
}
} catch (Exception e)
{ // TODO: handle exception
} finally
{ if (lock1.isHeldByCurrentThread())
{
lock1.unlock();
} if (lock2.isHeldByCurrentThread())
{
lock2.unlock();
}
System.out.println(Thread.currentThread().getId() + ":線程退出");
}
} public static void main(String[] args) throws InterruptedException {
Test t1 = new Test(1);
Test t2 = new Test(2);
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
thread1.start();
thread2.start();
Thread.sleep(1000); //DeadlockChecker.check();
} static class DeadlockChecker
{ private final static ThreadMXBean mbean = ManagementFactory
.getThreadMXBean(); final static Runnable deadlockChecker = new Runnable()
{ @Override
public void run()
{ // TODO Auto-generated method stub
while (true)
{ long[] deadlockedThreadIds = mbean.findDeadlockedThreads(); if (deadlockedThreadIds != null)
{
ThreadInfo[] threadInfos = mbean.getThreadInfo(deadlockedThreadIds); for (Thread t : Thread.getAllStackTraces().keySet())
{ for (int i = 0; i < threadInfos.length; i++)
{ if(t.getId() == threadInfos[i].getThreadId())
{
t.interrupt();
}
}
}
} try
{
Thread.sleep(5000);
} catch (Exception e)
{ // TODO: handle exception
}
}

}
};
public static void check()
{
Thread t = new Thread(deadlockChecker);
t.setDaemon(true);
t.start();
}
}

}

上述代碼有可能會發生死鎖,線程1得到lock1,線程2得到lock2,然後彼此又想獲得對方的鎖。

我們用jstack查看運行上述代碼後的情況

下面舉個例子:

package test;import java.util.concurrent.CyclicBarrier;public class Test implements Runnable{ private String soldier; private final CyclicBarrier cyclic; public Test(String soldier, CyclicBarrier cyclic)
{ this.soldier = soldier; this.cyclic = cyclic;
} @Override
public void run()
{ try
{ //等待所有士兵到齊
cyclic.await();
dowork(); //等待所有士兵完成工作
cyclic.await();
} catch (Exception e)
{ // TODO Auto-generated catch block
e.printStackTrace();
}

} private void dowork()
{ // TODO Auto-generated method stub
try
{
Thread.sleep(3000);
} catch (Exception e)
{ // TODO: handle exception
}
System.out.println(soldier + ": done");
} public static class BarrierRun implements Runnable
{ boolean flag; int n; public BarrierRun(boolean flag, int n)
{ super(); this.flag = flag; this.n = n;
} @Override
public void run()
{ if (flag)
{
System.out.println(n + "個任務完成");
} else
{
System.out.println(n + "個集合完成");
flag = true;
}

}

} public static void main(String[] args)
{ final int n = 10;
Thread[] threads = new Thread[n]; boolean flag = false;
CyclicBarrier barrier = new CyclicBarrier(n, new BarrierRun(flag, n));
System.out.println("集合"); for (int i = 0; i < n; i++)
{
System.out.println(i + "報道");
threads[i] = new Thread(new Test("士兵" + i, barrier));
threads[i].start();
}
}

}

列印結果:

集合

士兵5: done士兵7: done士兵8: done士兵3: done士兵4: done士兵1: done士兵6: done士兵2: done士兵0: done士兵9: done10個任務完成

1.7 LockSupport

提供線程阻塞原語

和suspend類似

LockSupport.park();
LockSupport.unpark(t1);

與suspend相比不容易引起線程凍結

LockSupport的思想呢,和Semaphore有點相似,內部有一個許可,park的時候拿掉這個許可,unpark的時候申請這個許可。所以如果unpark在park之前,是不會發生線程凍結的。

下面的代碼是[高並發Java 二] 多線程基礎中suspend示例代碼,在使用suspend時會發生死鎖。

而使用LockSupport則不會發生死鎖。

另外

park()能夠響應中斷,但不拋出異常。中斷響應的結果是,park()函數的返回,可以從Thread.interrupted()得到中斷標志。

在JDK當中有大量地方使用到了park,當然LockSupport的實現也是使用unsafe.park()來實現的。

public static void park() { unsafe.park(false, 0L);
}

1.8 ReentrantLock 的實現

下面來介紹下ReentrantLock的實現,ReentrantLock的實現主要由3部分組成:

『柒』 怎麼看JAVA開源項目的源碼

有個開源代碼託管平台叫github來了解下。

GitHub是一個面向開源及私有軟體項目的託管平台,因為只支持git作為唯一的版本庫格式進行託管,故名GitHub。

github也可察雀以是一個遠程代碼倉庫,你可以將你的代碼或者項目上傳到github倉庫,這個完全沒有問題,網上有github客戶端管理軟體,操作非常簡單,就類似於:SVN、CVS。

github也是一個開源代碼協作社區,通過github你可以參與別人的開源項目,也可以讓別人參與你的開源項目。有些公司的產品,自己不想投入人力,但又不想放棄,就採用github代碼託管的方式,將代碼開源出去,讓開發愛好者參與進來,其中docker就是一個很好的例子,也是開源最成功的一個項目。

下面介紹如何從github上拿到開源項目:zxin。

1、打開github官網「」。

2、根據「拍沒知zxing」查找。

3、下載開源項目:zxing

操作:Cloneordowanload->DownloadZIP.

這樣zxin源代碼就拿到了,doc目錄是項目文檔襲消,打開

index.html,全是英文,

要能讀懂源代碼,需要有很好的英文閱讀能力,祝你好運。

『捌』 以太坊源碼分析--p2p節點發現

節點發現功能主要涉及 Server Table udp 這幾個數據結構,它們有獨自的事件響應循環,節點發現功能便是它們互相協作完成的。其中,每個以太坊客戶端啟動後都會在本地運行一個 Server ,並將網路拓撲中相鄰的節點視為 Node ,而 Table Node 的容器, udp 則是負責維持底層的連接。下面重點描述它們中重要的欄位和事件循環處理的關鍵部分。

PrivateKey - 本節點的私鑰,用於與其他節點建立時的握手協商
Protocols - 支持的所有上層協議
StaticNodes - 預設的靜態 Peer ,節點啟動時會首先去向它們發起連接,建立鄰居關系
newTransport - 下層傳輸層實現,定義握手過程中的數據加密解密方式,默認的傳輸層實現是用 newRLPX() 創建的 rlpx ,這不是本文的重點
ntab - 典型實現是 Table ,所有 peer Node 的形式存放在 Table
ourHandshake - 與其他節點建立連接時的握手信息,包含本地節點的版本號以及支持的上層協議
addpeer - 連接握手完成後,連接過程通過這個通道通知 Server

Server 的監聽循環,啟動底層監聽socket,當收到連接請求時,Accept後調用 setupConn() 開始連接建立過程

Server的主要事件處理和功能實現循環

Node 唯一表示網路上的一個節點

IP - IP地址
UDP/TCP - 連接使用的UDP/TCP埠號
ID - 以太坊網路中唯一標識一個節點,本質上是一個橢圓曲線公鑰(PublicKey),與 Server 的 PrivateKey 對應。一個節點的IP地址不一定是固定的,但ID是唯一的。
sha - 用於節點間的距離計算

Table 主要用來管理與本節點與其他節點的連接的建立更新刪除

bucket - 所有 peer 按與本節點的距離遠近放在不同的桶(bucket)中,詳見之後的 節點維護
refreshReq - 更新 Table 請求通道

Table 的主要事件循環,主要負責控制 refresh revalidate 過程。
refresh.C - 定時(30s)啟動Peer刷新過程的定時器
refreshReq - 接收其他線程投遞到 Table 的 刷新Peer連接 的通知,當收到該通知時啟動更新,詳見之後的 更新鄰居關系
revalidate.C - 定時重新檢查以連接節點的有效性的定時器,詳見之後的 探活檢測

udp 負責節點間通信的底層消息控制,是 Table 運行的 Kademlia 協議的底層組件

conn - 底層監聽埠的連接
addpending - udp 用來接收 pending 的channel。使用場景為:當我們向其他節點發送數據包後(packet)後可能會期待收到它的回復,pending用來記錄一次這種還沒有到來的回復。舉個例子,當我們發送ping包時,總是期待對方回復pong包。這時就可以將構造一個pending結構,其中包含期待接收的pong包的信息以及對應的callback函數,將這個pengding投遞到udp的這個channel。 udp 在收到匹配的pong後,執行預設的callback。
gotreply - udp 用來接收其他節點回復的通道,配合上面的addpending,收到回復後,遍歷已有的pending鏈表,看是否有匹配的pending。
Table - 和 Server 中的ntab是同一個 Table

udp 的處理循環,負責控制消息的向上遞交和收發控制

udp 的底層接受數據包循環,負責接收其他節點的 packet

以太坊使用 Kademlia 分布式路由存儲協議來進行網路拓撲維護,了解該協議建議先閱讀 易懂分布式 。更權威的資料可以查看 wiki 。總的來說該協議:

源碼中由 Table 結構保存所有 bucket bucket 結構如下

節點可以在 entries replacements 互相轉化,一個 entries 節點如果 Validate 失敗,那麼它會被原本將一個原本在 replacements 數組的節點替換。

有效性檢測就是利用 ping 消息進行探活操作。 Table.loop() 啟動了一個定時器(0~10s),定期隨機選擇一個bucket,向其 entries 中末尾的節點發送 ping 消息,如果對方回應了 pong ,則探活成功。

Table.loop() 會定期(定時器超時)或不定期(收到refreshReq)地進行更新鄰居關系(發現新鄰居),兩者都調用 doRefresh() 方法,該方法對在網路上查找離自身和三個隨機節點最近的若干個節點。

Table 的 lookup() 方法用來實現節點查找目標節點,它的實現就是 Kademlia 協議,通過節點間的接力,一步一步接近目標。

當一個節點啟動後,它會首先向配置的靜態節點發起連接,發起連接的過程稱為 Dial ,源碼中通過創建 dialTask 跟蹤這個過程

dialTask表示一次向其他節點主動發起連接的任務

在 Server 啟動時,會調用 newDialState() 根據預配置的 StaticNodes 初始化一批 dialTask , 並在 Server.run() 方法中,啟動這些這些任務。

Dial 過程需要知道目標節點( dest )的IP地址,如果不知道的話,就要先使用 recolve() 解析出目標的IP地址,怎麼解析?就是先要用藉助 Kademlia 協議在網路中查找目標節點。

當得到目標節點的IP後,下一步便是建立連接,這是通過 dialTask.dial() 建立連接

連接建立的握手過程分為兩個階段,在在 SetupConn() 中實現
第一階段為 ECDH密鑰建立 :

第二階段為協議握手,互相交換支持的上層協議

如果兩次握手都通過,dialTask將向 Server 的 addpeer 通道發送 peer 的信息

『玖』 ValueAnimator源碼解析-基於Android API30

先上個時序圖,整個調用鏈都在扒薯圖里了。

ValueAnimator.java

初始化動畫,並將監聽添加到AnimationHandler

AnimationHandler.java

將Frame監聽添加到Choreographer

Choreographer.java

請求下一個信號,不明白信號的可看 《Android 底層渲染 - 屏幕刷新機制源碼分析》

FrameDisplayEventReceiver.java

當來了信號量後,執行onvsync,發送Handler同步消息,Message的Callback就是FrameDisplayEventReceiver,最終通過Handler執行了FrameDisplayEventReceiver.run()方法

DisplayEventReceiver.java

請求信號量,並分發處理

FrameDisplayEventReceiver.java

處理回調
Choreographer.CallbackRecord.java

執行Frame回調

AnimationHandler.java

幀回調到動畫回調
ValueAnimator.java

整個流程就分析完了。動畫鋒此尺的核心驅動是,利用屏幕的刷新機制,請求信號,然後在通過Handler的同步消息,執行Frame回調。Frame回調中在執行動畫回調。動畫回調中根據時間和動畫插值。計算出最新的動畫值,回調給用戶。ValueAnimation中animateBasedOnTime方法會返回當前動畫是否銀高結束,如果已經結束就移除動畫回調,如果未結束FrameCallback的doFrame中處理完這一幀後,會繼續請求下一個信號量。

『拾』 為什麼大多數區塊鏈項目不使用java開發

區塊鏈項目對效率的要求比較高,所以大多數核心源碼的開發都是使用c/c++。但是如果是做都區塊鏈項目,除非要對源代碼進行大量的調整,否則也不見得就不選擇使用java。一般的dapp應用,使用java開發應該也是不錯的選擇。比如以太坊區塊鏈的話,針對java的有web3j的類庫,十分方便;比特幣的話有bitcoinj類庫,也很好用。還是要看還是什麼級別的應用,要做什麼,以及團隊的情況吧。

分享兩個java區塊鏈教程:

  1. java比特幣詳解

  2. java以太坊開發

閱讀全文

與以太坊java源碼分析相關的資料

熱點內容
比特幣最貴價格多少錢一個 瀏覽:330
深圳數字貨幣在哪裡申請 瀏覽:162
比特幣最早在中國的體現 瀏覽:410
比特幣美國情報局 瀏覽:475
怎樣注銷數字貨幣賬戶 瀏覽:415
被騙充值花椒虛擬貨幣 瀏覽:681
虛擬貨幣拉人套路 瀏覽:648
比特幣礦機靜音 瀏覽:323
ht和btc有什麼不同 瀏覽:212
注冊數字貨幣不往裡打錢 瀏覽:378
除法算力怎麼寫 瀏覽:471
比特幣軟體叫什麼 瀏覽:904
河南雲享區塊鏈科技有限公司 瀏覽:976
中國幣虛擬貨幣總量 瀏覽:557
游戲虛擬貨幣可以兌換獎品 瀏覽:536
以太坊轉過去能報案嗎 瀏覽:707
北京證監局打擊虛擬貨幣 瀏覽:557
以太坊取幣怎麼慢了 瀏覽:800
以太坊走偏了老冒 瀏覽:488
比特幣的價值是如何測算的 瀏覽:570