手把手教你學Numpy,這些api不容錯過

本文始發於個人公眾號:TechFlow,原創不易,求個關注

今天是Numpy專題的第5篇文章,我們來繼續學習Numpy當中一些常用的數學和統計函數。

基本統計方法

在日常的工作當中,我們經常需要通過一系列值來了解特徵的分佈情況。比較常用的有均值、方差、標準差、百分位數等等。前面幾個都比較好理解,簡單介紹一下這個百分位數,它是指將元素從小到大排列之後,排在第x%位上的值。我們一般常用的是25%,50%和75%這三個值,通過這幾個值,我們很容易對於整個特徵的分佈有一個大概的了解。

前面三個指標:均值、方差、標準差都很好理解,我們直接看代碼就行。

median和percentile分別是求中位數與百分位數,它們不是Numpy當中array的函數,而是numpy的庫函數。所以我們需要把array當做參數傳入。percentile這個函數還需要額外傳入一個int,表示我們想要得到的百分位數,比如我們想要知道50%位置上的數,則輸入50。

除了這些之外,我們還會經常用到sum,min,max,argmin,argmax這幾個函數。sum,min,max很好理解,argmin和argmax的意思是獲取最小值和最大值的索引

這裏返回的索引有點奇怪,和我們想的不同,居然不是一個二維的索引而是一維的。實際上numpy的內部會將高維數組轉化成一維之後再進行這個操作,我們可以reshape一下數組來進行驗證:

這些只是api的基本用法,numpy當中支持的功能不僅如此。我們觀察一下這些函數會發現,它們的作用域都是一組數據,返回的是一組數據通過某種運算得到的結果。舉個例子,比如sum,是對一組數據的價格。std計算的是一組數據的標準差,這樣的函數我們稱為聚合函數

numpy當中的聚合函數在使用的時候允許傳入軸這個參數,限制它聚合的範圍。我們通過axis這個參數來控制,axis=0表示對列聚合,axis=1表示對行聚合。我們死記的話總是會搞混淆,實際上axis傳入的也是一個索引,表示第幾個索引的索引。我們的二維數組的shape是[行, 列],其中的第0位是行,第1位是列,可以認為axis是這個索引向量的一個索引。

我們可以來驗證一下:

可以看到axis=0和axis=1返回的向量的長度是不同的,因為以列為單位聚合只有4列,所以得到的是一個1 x 4的結果。而以行為單位聚合有5行,所以是一個1 x 5的向量。

除了上面介紹的這些函數之外,還有cumsum和cumprod這兩個api。其中cumsum是用來對數組進行累加運算,而cumprod是進行的累乘運算。只是在實際工作當中,很少用到,我就不展開細講了,感興趣的同學可以查閱api文檔了解一下。

bool數組的方法

我們之前在Python的入門文章當中曾經提到過,在Python中True和False完全等價於1和0。那麼在上面這些計算的方法當中,如果存在bool類型的值,都會被轉化成1和0進行的計算。

我們靈活運用這點會非常方便,舉個例子,假設我們要統計一批數據當中有多少條大於0。我們利用sum會非常方便:

bool數組除了可以應用上面這些基本的運算api之外,還有專門的兩個api,也非常方便。一個叫做any,一個叫做all。any的意思是只要數組當中有一個是True,那麼結果就是True。可以認為是Is there any True in the array的意思,同樣,all就是說只有數組當中都是True,結果才是True。對應的英文自然是Are the values in the array all True。

這個只要理解了,基本上很難忘記。

排序

Python原生的數組可以排序,numpy當中的數組自然也不例外。我們只需要調用sort方法就可以排序了,不過有一點需要注意,numpy中的sort默認是一個inplace的方法。也就是說我們調用完了sort之後,原數組的值就自動變化了。

如果寫成了arr = arr.sort()會得到一個None,千萬要注意。

同樣,我們也可以通過傳入軸這個參數來控制它的排序範圍,可以做到對每一列排序或者是對每一行排序,我們來看個例子:

這個是對列排序,如果傳入0則是對行排序,這個應該不難理解。

集合api

numpy當中還提供了一些面向集合的api,相比於針對各種計算的api,這些方法用到的情況比較少。常用的一般只有unique和in1d

unique顧名思義就是去重的api,可以返回一維array去重且排序之後的結果。我們來看個例子:

它等價於:

set(sorted(arr))

in1d是用來判斷集合內的元素是否在另外一個集合當中,函數會返回一個bool型的數組。我們也可以來看個例子:

除了這兩個api之外,還有像是計算並集並排序的union1d,計算差集的setdiff1d,計算兩個集合交集並排序的intersect1d等等。這些api的使用頻率實在是不高,所以就不贅述了。用到的時候再去查閱即可。

總結

今天我們聊了numpy當中很多常用的計算api,這些api在我們日常做機器學習和數據分析的時候經常用到。比如分析特徵分佈的時候,如果數據量很大是不適合作圖或者是可視化觀察的。這個時候可以從中位數、均值、方差和幾個關鍵百分位點入手,再比如在我們使用softmax多分類的時候,也會用到argmax來獲取分類的結果。

總之,今天的內容非常關鍵,在numpy整體的應用當中佔比很高,希望大家都能熟悉它們的基本用法。這樣即使以後忘記,用到的時候再查閱也還來得及。

今天的文章就是這些,如果喜歡本文,可以的話請點個關注,給我一點鼓勵,也方便獲取更多文章。

本文使用 mdnice 排版

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

Redis的持久化設計

Redis 持久化設計

持久化的功能:Redis是內存數據庫,數據都是存儲在內存中的,為了避免進程退出導致數據的永久丟失,要定期將Redis中的數據以某種形式從內存保存到硬盤,當下次Reids重啟時,利用持久化文件實現數據恢復。

RDB:將當前數據保存到硬盤

AOF:將每次執行的寫命令保存到硬盤(類似MySQL的binlog)

1. RDB持久化

RDB持久化是將當前進程中的數據生成快照保存到硬盤(因此也稱作快照持久化),保存的文件後綴是rdb;當Redis重新啟動時,可以讀取快照文件恢複數據。

  1. 觸發條件

    • 手動觸發 save 命令和bgsave命令都可以生成RDB文件, save命令會阻塞Redis服務進程,知道RDB文件創建完畢,bgsave命令則是創建一個子進程,由子進程來負責創建RDB文件,父進程繼續處理請求,bgsave命令執行過程中,只有fork子進程時會阻塞服務器,而對於save命令,整個過程都會阻塞服務器,因此save已基本被廢棄,線上環境要杜絕save的使用;後文中也將只介紹bgsave命令。此外,在自動觸發RDB持久化時,Redis也會選擇bgsave而不是save來進行持久化

    SAVE 執行期間,AOF 寫入可以在後台線程進行,BGREWRITEAOF 可以在子進程進行,所以這三種操作可以同時進行 ,為了避免性能問題,BGSAVE 和 BGREWRITEAOF 不能同時執行

  2. 自動觸發

save m n

在配置文件中通過 save m n 命令,指定當前m秒內發生n次變化時,觸發bgsave。

​ 其中save 900 1的含義是:當時間到900秒時,如果redis數據發生了至少1次變化,則執行bgsave;save 300 10和save 60 10000同理。當三個save條件滿足任意一個時,都會引起bgsave的調用.

Redis的save m n,是通過serverCron函數、dirty計數器、和lastsave時間戳來實現的-

  • serverCron函數,是Redis服務器的周期性操作函數,默認每隔100ms執行一次,該函數對服務器的狀態進行維護,其中一項工作就是檢測save m n 配置是否滿足條件,如果滿足就執行bgsave.
  • dirty計數器 記錄服務器進行了多少起操作,修改,不是客戶端執行了多少修改數據的命令
  • lastsave時間戳也是Reids服務器維持的一個狀態,記錄上一次成功執行bgsave的時間

save m n的原理如下:每隔100ms,執行serverCron函數;在serverCron函數中,遍歷save m n配置的保存條件,只要有一個條件滿足,就進行bgsave。對於每一個save m n條件,只有下面兩條同時滿足時才算滿足:

  • 當前時間-lastsave > m

  • dirty >= n

在主從複製場景下,如果從節點執行全量複製操作,則主節點會執行bgsave命令,並將rdb文件發送給從節點。

在執行shutdown命令時,自動執行rdb持久化

1.2 RDB文件

設置存儲路徑

- 配置文件:dir配置指定目錄,dbfilename指定文件名。默認是Redis根目錄下的dump.rdb文件
- 動態設置: 

config set dir {newdir} /// config set dbfilename {newFileName}

RDB文件 是經過壓縮的二進制文件,默認採用LZF算法對RDB文件進行壓縮,雖然壓縮耗時,但是可以大大減小文件體積,默認是開啟的,可以通過命令關閉:

config set rdbcompression no

RDB文件的壓縮並不是針對整個文件進行的,而是對數據庫中的字符串進行的,且只有在字符串達到一定長度(20字節)時才會進行

格式:

字段說明:

  1. REDIS常量,保存‘REDIS’5個字符

  2. db_version RDB文件的版本號

  3. SELECTDB 表示一個完整的數據庫(0號數據庫),同理SELECTDB 3 pairs表示完整的3號數據庫;只有當數據庫中有鍵值對時,RDB文件中才會有該數據庫的信息(上圖所示的Redis中只有0號和3號數據庫有鍵值對);如果Redis中所有的數據庫都沒有鍵值對,則這一部分直接省略。其中:SELECTDB是一個常量,代表後面跟着的是數據庫號碼;0和3是數據庫號碼;

  4. KEY-VALUE-PAIRS: pairs則存儲了具體的鍵值對信息,包括key、value值,及其數據類型、內部編碼、過期時間、壓縮信息等等

  1. EOF 標志著數據庫內容的結尾(不是文件的結尾),值為 rdb.h/EDIS_RDB_OPCODE_EOF (255)

  2. CHECK-SUM RDB 文件所有內容的校驗和,一個 uint_64t 類型值, REDIS 在寫入 RDB 文件時將校驗和保存在 RDB 文件的末尾,當讀取時,根據它的值對內容進行校驗

。如果這個域的值為 0 ,那麼表示 Redis 關閉了校驗和功能。

1.3 啟動時加載

​ RDB文件的載入工作是在服務器啟動時自動執行的,並沒有專門的命令。但是由於AOF的優先級更高,因此當AOF開啟時,Redis會優先載入AOF文件來恢複數據;只有當AOF關閉時,才會在Redis服務器啟動時檢測RDB文件,並自動載入。服務器載入RDB文件期間處於阻塞狀態,直到載入完成為止

2. AOF持久化

AOF(Append Only File) 則以協議文本的方式,將所有對數據庫進行過寫入的命令(及其參數)記錄到 AOF
文件,以此達到記錄數據庫狀態的目的

2.1 開啟AOF

Redis服務器默認開啟RDB,關閉AOF;要開啟AOF,需要在配置文件中配置:

appendonly yes

2.2 執行流程

2.2.1 命令寫入緩衝區

//緩衝區的定義 是一個SDS, 可以兼容C語言的字符串
struct redisServer {
    // AOF緩衝區, 在進入事件loop之前寫入
    sds aof_buf;
};
  1. 命令傳播: Redis將執行完的命令、命令的參數、命令的參數個數等信息發送到 AOF 程序中

  2. 緩存追加: AOF程序根據接收到的命令命令數據,將命令轉換為網絡通訊協議的格式,然後將協議內容追加到服務器的 AOF 緩存中。

    • 將命令以文本協議格式保存在緩存中
    • 為什麼使用文本協議格式?兼容性,避免二次開銷,可讀性
    • 為什麼寫入緩存?這樣不會受制於磁盤的IO性能,避免每次有寫命令都直接寫入硬盤,導致硬盤IO成為Redis負載的瓶頸
  3. 文件寫入和保存:AOF 緩存中的內容被寫入到 AOF 文件末尾,如果設定的 AOF 保存
    條件被滿足的話,fsync 函數或者 fdatasync 函數會被調用,將寫入的內容真正地保存到磁盤中。

    為了提高文件寫入效率,在現代操作系統中,當用戶調用write函數將數據寫入文件時,操作系統通常會將數據暫存到一個內存緩衝區里,當緩衝區被填滿或超過了指定時限后,才真正將緩衝區的數據寫入到硬盤裡。這樣的操作雖然提高了效率,但也帶來了安全問題:如果計算機停機,內存緩衝區中的數據會丟失;因此系統同時提供了fsync、fdatasync等同步函數,可以強制操作系統立刻將緩衝區中的數據寫入到硬盤裡,從而確保數據的安全性。

    AOF保存模式:

    • AOF_FSYNC_ALWAYS: 命令寫入aof-buf后立即調用系統的fsync操作同步到AOF文件。因為 SAVE 是由 Redis 主進程執行的,所以在 SAVE 執行期間,主進程會被阻塞,不能接受命令請求。這種情況下,每次有寫命令都要同步到AOF文件,硬盤IO成為性能瓶頸,Redis只能支持大約幾百TPS寫入,嚴重降低了Redis的性能;即便是使用固態硬盤(SSD),每秒大約也只能處理幾萬個命令,而且會大大降低SSD的壽命。
    • AOF_FSYNC_NO: 命令寫入aof_buf后調用系統write操作,不對AOF文件做fsync同步;同步由操作系統負責,通常同步周期為30秒。這種情況下,文件同步的時間不可控,且緩衝區中堆積的數據會很多,數據安全性無法保證。
    • AOF_FSYNC_EVERYSEC: 每一秒鐘保存一次,命令寫入aof_buf后調用系統write操作, write完成后線程返回, fsync同步文件操作由專門線程每秒調用一次

2.2.2. 文件重寫

隨着命令不斷寫入AOF,文件會越來越大,為了解決這個問題,Redis引入AOF重寫機制壓縮文件體積,AOF文件重寫是把Redis進程內的數據轉化為寫命令同步到新AOF文件的過程。

重寫后的AOF文件為什麼可以變小?

  1. 進程內已經超時的數據不再寫入文件
  2. 舊的AOF文件含有無效命令 ,如有些數據被重複設值(set mykey v1, set mykey v2)、有些數據被刪除了(sadd myset v1, del myset)等等, 新的AOF文件只保留最終的數據寫入命令
  3. 多條寫入命令可以合併為一個,如:lpush list a、lpush list b可以轉化為:lpush list a b。為了防止單條命令過大造成客戶端緩衝區溢出,對於list、set、hash等類型操作,以64個元素為邊界拆分為多條

AOF重寫可以手動觸發也可以自動觸發:

  • 手動觸發: 直接調用bgrewriteaof命令
  • 自動觸發:根據auto-aof-rewrite-min-size和auto-aof-rewrite-percentage參數確定自動觸發時機。
    • auto-aof-rewrite-min-size:表示運行AOF重寫時文件最小體積,默認為64MB
    • auto-aof-rewrite-percentage:代表當前AOF文件空間(aof_current_size)和上一次重寫后AOF文件空間(aof_base_size)的比值

流程說明:

1)執行AOF重寫請求。

如果當前進程正在執行AOF重寫,請求不執行。

如果當前進程正在執行bgsave操作,重寫命令延遲到bgsave完成之後再執行。

2)父進程執行fork創建子進程,開銷等同於bgsave過程。

3.1)主進程fork操作完成后,繼續響應其它命令。

  所有修改命令依然寫入AOF文件緩衝區並根據appendfsync策略同步到磁盤,保證原有AOF機制正確性。

3.2)由於fork操作運用寫時複製技術,子進程只能共享fork操作時的內存數據

  由於父進程依然響應命令,Redis使用“AOF”重寫緩衝區保存這部分新數據,防止新的AOF文件生成期間丟失這部分數據。

4)子進程依據內存快照,按照命令合併規則寫入到新的AOF文件。

  每次批量寫入硬盤數據量由配置aof-rewrite-incremental-fsync控制,默認為32MB,防止單次刷盤數據過多造成硬盤阻塞。

5.1)新AOF文件寫入完成后,子進程發送信號給父進程,父進程調用一個信號處理函數,並執行以前操作更新統計信息。

5.2)父進程把AOF重寫緩衝區的數據寫入到新的AOF文件。這時新 AOF 文件所保存的數據庫狀態將和服務器當前的數據庫狀態一致。

5.3)對新的AOF文件進行改名,原子地(atomic)覆蓋現有的AOF文件,完成新舊文件的替換。

在整個 AOF 後台重寫過程中,只有信號處理函數執行時會對服務器進程(父進程)造成阻塞,其他時候,AOF 後台重寫都不會阻塞父進程,這將 AOF 重寫對服務器性能造成的影響降到了最低

參考《Redis-設計與實現:AOF-持久化》

2.2.3 重啟加載

流程說明:

1)AOF持久化開啟且存在AOF文件時,優先加載AOF文件。

2)AOF關閉或者AOF文件不存在時,加載RDB文件。

3)加載AOF/RDB文件成功后,Redis啟動成功。

4)AOF/RDB文件存在錯誤時,Redis啟動失敗並打印錯誤信息。

數據還原的詳細步驟:

  1. 創建一個不帶網絡連接的偽客戶端(fake client): 因為 Redis 的命令只能在客戶端上下文中執行,而載入 AOF 文件時所使用的命令直接來源於 AOF 文件而不是網絡連接,所以服務器使用了一個沒有網絡連接的偽客戶端來執行 AOF 文件保存的寫命令,偽客戶端執行命令的效果和帶網絡連接的客戶端執行命令的效果完全一樣。
  2. 從AOF文件中分析並讀取出一條寫命令,使用偽客戶端執行被讀出的寫命令,重複此操作,直到AOF文件中的所有寫命令都被處理完畢為止。

2.2.4 文件校驗

加載損壞的AOF文件會拒絕啟動,並打印錯誤信息。

注意:對於錯誤格式的AOF文件,先進性備份,然後採用redis-check-aof --fix命令進行修復,修復后使用diff -u對比數據差異,找到丟失的數據,有些可以進行人工補全。

AOF文件可能存在結尾不完整的情況,比如機器突然掉電導致AOF尾部文件命令寫入不全。

Redis為我們提高了aof-load-truncated配置來兼容這種情況,默認開啟

3. 了解MySQL中的binlog

mysql binlog應用場景與原理深度剖析

參考博文與書籍:

  1. 《redis設計與實現》
  2. Redis持久化
  3. [徐劉根-Redis實戰和核心原理詳解(8)使用快照RDB和AOF將Redis數據持久化到硬盤中](https://blog.csdn.net/xlgen157387/article/details/61925524

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

無異常日誌,就不能排查問題了???

小聲逼逼

眾所周知,日誌是排查問題的重要手段。關於日誌設計,以及怎麼根據從【用戶報障】環節開始到秒級定位問題這個我們下一期說(絕非套路),這一期,主要講一下,在沒有異常日誌的情況下,如何定位問題。沒有日誌當真能排查問題,不會是標題黨吧!

案例一

從最大的同性交友網站中拉取【dubbo-spring-boot-project】的代碼。

然後把demo跑起來。

本場景是由真實案例改編,因為公司代碼比較複雜也不方便透露,而這個demo在github上大家都能找到,既保證了原汁原味,又能讓大家方便自己體驗排查過程。

好了,我們先設置owner = "feichao",然後看一下控制台

一切正常

那麼,當我設置成owner = "feichaozhenshuai!",再啟動

看似一切都正常,那麼,我們到控制台一看。

什麼情況,怎麼就沒owner了?

這是在哪個環節出問題了?其實肥朝當初在公司遇到這個問題的時候,場景比這個複雜得多。因為公司的業務里沒有owner的話,在運行時會出現一些其他異常,涉及公司業務這裏就不展開了,我們言歸正傳,為毛我設置成feichaozhenshuai!就不行了,那我設置成肥朝大帥比電腦會不會爆炸啊???

常見的錯誤做法是,把這個問題截圖往群里一丟,問“你們有沒有遇到過dubbo裏面,owner設置不生效的問題?”

而關注了肥朝公眾號的【真愛粉絲】會這麼問,“dubbo裏面設置owner卻不生效,你們覺得我要從個角度排查問題?”。一看到這麼正確的提問方式,我覺得我不回復你都不好意思。好了,回到主題,這個時候,沒有一點點錯誤日誌,但是卻設置不成功,我們有哪些排查手段?

套路一

直接找set方法,看看是不是代碼做了判斷,防止在owner字段裏面set肥朝真帥這種詞語,避免把帥這件事走漏風聲!。這麼一分析似乎挺有道理對吧,那麼,如何快速找到這個set方法呢?如圖

public void setOwner(String owner) {
    checkMultiName("owner", owner);
    this.owner = owner;
}

我們跟進checkMultiName代碼后發現

protected static void checkProperty(String property, String value, int maxlength, Pattern pattern) {
    if (StringUtils.isEmpty(value)) {
        return;
    }
    if (value.length() > maxlength) {
        throw new IllegalStateException("Invalid " + property + "=\"" + value + "\" is longer than " + maxlength);
    }
    if (pattern != null) {
        Matcher matcher = pattern.matcher(value);
        if (!matcher.matches()) {
            throw new IllegalStateException("Invalid " + property + "=\"" + value + "\" contains illegal " +
                    "character, only digit, letter, '-', '_' or '.' is legal.");
        }
    }
}

從異常描述就很明顯可以看出,原來owner裏面是只支持-_等這類特殊符號,!是不支持的,所以設置成不成功,和肥朝帥不帥是沒關係的,和後面的!是有關係的。擦,原來是肥朝想多了,給自己加戲了!!!

當然肥朝可以告訴你,在後面的版本,修復了這個bug,日誌會看得到異常了。這個時候你覺得問題就解決了?

我相信此時很多假粉就會關掉文章,或者說下次肥朝發了一些他們不喜歡看的文章(你懂的)后,他們就從此取關,但是肥朝想說,且慢動手!!!

你想嘛,萬一你以後又遇到類似的問題呢?而且源碼層次很深,就不是簡單的搜個set方法這麼簡單,這次給你搜到了set方法並解決問題,簡直是偶然成功。因此,我才多次強調,要持續關注肥朝,掌握更多套路。這難道是想騙你關注?我這分明是愛你啊!

那麼,萬一以後遇到一些吞掉異常,亦或者某些原因導致日誌沒打印,我們到底如何排查?

套路二

我們知道idea裏面有很多好用的功能,比如肥朝之前的【看源碼,我為何推薦idea?】中就提到了條件斷點,除此之外,還有一個被大家低估的功能,叫做異常斷點

肥朝掃了一眼,裏面的單詞都是小學的英語單詞,因此怎麼使用就不做過多解釋。遇到這個問題時,我們可以這樣設置異常斷點。

運行起來如下:

這樣,運行起來的時候,就會迅速定位到異常位置。然後一頓分析,應該很容易找出問題。

是不是有點感覺了?那我們再來一個題型練習一下。

案例二

我們先在看之前肥朝粉絲群的提問

考慮到部分粉絲不在群里,我就簡單描述一下這個粉絲的問題,他代碼有個異常,然後catch打異常日誌,但是日誌卻沒輸出。

當然你還是不理解也沒關係,我根據該粉絲的問題,給你搭建了一個最簡模型的demo,模型雖然簡單,但是問題是同樣的,原汁原味,熟悉的配方,熟悉的味道。git地址如下:【https://gitee.com/HelloToby/springboot-run-exception】

我們運行起來看一下

@Slf4j
public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {

    public HelloSpringApplicationRunListener(SpringApplication application, String[] args) {
    }

    @Override
    public void starting() {

    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {

    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        throw new RuntimeException("歡迎關注微信公眾號【肥朝】");
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {

    }

    @Override
    public void finished(ConfigurableApplicationContext context, Throwable exception) {
    }
}

你會發現,一運行起來進程就停止,一點日誌都沒。絕大部分假粉絲遇到這個情況,都是菊花一緊,一點頭緒都沒,又去群里問”你們有沒有遇到過,Springboot一起來進程就沒了,但是沒有日誌的問題?“。正確提問姿勢肥朝已經強調過,這裏不多說。那麼我們用前面學到的排查套路,再來走一波

我們根據異常棧順藤摸瓜

我們從代碼中看出兩個關鍵單詞【reportFailure】、【context.close()】,經過斷點我們發現,確實是會先打印日誌,再關掉容器。但是為啥日誌先執行,再關掉容器,日誌沒輸出,容器就關掉了呢?因為,這個demo中,日誌是全異步日誌,異步日誌還沒執行,容器就關了,導致了日誌沒有輸出。

該粉絲遇到的問題是類似的,他是單元測試中,代碼中的異步日誌還沒輸出,單元測試執行完進程就停止了。知道了原理解決起來也很簡單,比如最簡單的,跑單元測試的時候末尾先sleep一下等日誌輸出。

在使用Springboot中,其實經常會遇到這種,啟動期間出現異常,但是日誌是異步的,日誌還沒輸出就容器停止,導致沒有異常日誌。知道了原理之後,要徹底解決這類問題,可以增加一個SpringApplicationRunListener

/**
 * 負責應用啟動時的異常輸出
 */
@Slf4j
public class OutstandingExceptionReporter implements SpringApplicationRunListener {

    public OutstandingExceptionReporter(SpringApplication application, String[] args) {
    }

    @Override
    public void starting() {

    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {

    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {

    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {

    }

    @Override
    public void finished(ConfigurableApplicationContext context, Throwable exception) {
        if (exception != null) {
            log.error("application started failed",exception);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                log.error("application started failed", e);
            }
        }
    }
}

再啰嗦一句,其實日誌輸出不了,除了這個異步日誌的案例外,還有很多情況的,比如日誌衝突之類的,排查套路還很多,因此,建議持續關注,每一個套路,都想和你分享!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

滅絕時代將來臨? WWF:50年來全球野生動物數量驟減2/3

摘錄自2020年9月16日民視新聞報導

全球最大的非政府環境保護組織「世界自然基金會」,最近發布了地球生命力報告2020,顯示近半世紀來,全球野生動物種群數量,已平均銳減68%,生物多樣性消失,人類難辭其咎,像是巴西中西部大沼澤的林火,疑似是人為的火災演變成的森林大火,目前就燒毀了70%美洲虎的主要棲息地。

中南美洲的物種及全球淡水棲息地受到的衝擊尤其嚴重,平均分別下降了94%和84%,世界自然基金會總幹事藍柏堤尼表示,「不到50年我們就看到銳減2/3的野生動物,相較於這些動物棲息在地球上數百萬年,這不過是一眨眼的工夫,第二個原因必須擔憂的是,我們看到過去20年來加速惡化,上次地球生命力報告發布時說的是近6成,但現在是7成。」

生物多樣性
國際新聞

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

8分之1的歐洲人口死於環境污染 空污、噪音通通上榜

摘錄自2020年9月15日自由時報報導

根據歐洲環境署的一項調查研究報告,歐盟人口大約有8分之1的死與環境污染有關;對此,歐洲環境署負責人建議,優先保護社會上最受威脅的族群,解決貧困問題。

歐洲環境署補充,化學物質、過量使用抗生素後對病原體造成的抗藥性以及被污染的飲用水,上述也是導致人們過早死亡的因素,而在許多東歐國家,環境因素導致的過早死亡率比西歐高得多。

根據世衛組織的數據,歐盟國家過早死亡的比例大約為13%,相當於每年有63萬人過早死亡,不幸的是,環境因素(例如:癌症、心臟病和中風)造成的死亡原本是可以避免。

歐洲環境署負責人漢斯.布魯尼克斯(Hans Bruyninckx)建議,必須採取措施來保護社會上最受威脅的族群,而貧困通常是主要問題,因為它會帶來惡劣的環境和影響健康狀況,因此為來歐洲未來可以在此多著墨,如此一來才能真正解決環境與健康的問題。

污染治理
國際新聞
歐洲
空污
噪音

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

2020科學界聯合報告:武肺未阻氣候變遷 溫室氣體濃度創300萬年新高

環境資訊中心外電;姜唯 翻譯;林大利 審校;稿源:ENS

儘管全球為防堵武漢肺炎(COVID-19)而大規模封城,大氣中的溫室氣體濃度卻仍來到300萬年來最高。

氣候變遷沒有因為武漢肺炎而停下腳步。封城和經濟趨緩雖使碳排放出現暫時性下降,整體趨勢仍朝著肺炎爆發前的水準邁進。

2020年二氧化碳排放量將因為疫情關係減少4%至7%。確切能減少多少將取決於疫情控制情況和政府的應對措施。

今年雖然碰上疫情而大規模封城,大氣中的溫室氣體濃度卻仍來到300萬年來最高。照片來源:Tony Webster(CC BY-SA 2.0)

2016至2020年將是有史以來最熱的五年

全球最大、最具權威性的多個科學組織合作發表「2020科學界聯合報告(United in Science 2020)」,彙整出全面性的相關資訊。

這份報告是本系列報告的第二份,由世界氣象組織(World Meteorological Organization﹐WMO)協調,收集來自全球碳計畫(Global Carbon Project)、政府間氣候變遷專門委員會(Intergovernmental Panel on Climate Change)、聯合國教科文組織政府間海洋學委員會(Intergovernmental Oceanographic Commission of UNESCO)、聯合國環境規劃署(UN Environment Programme﹐UNEP)和英國氣象局的專業意見 。

「溫室氣體濃度已經達到300萬年來的最高水準,並持續上升中。同時在2020年上半年,西伯利亞大片地區出現長時間的異常熱浪,若不是人為的氣候變遷,這幾乎不可能發生。2016至2020年將是有史以來最熱的五年。」WMO秘書長塔拉斯(Petteri Taalas)教授警告,「這份報告說明了,儘管我們的生活在2020年多方被打亂,但氣候變遷的影響力並未減弱。」

暖化趨勢很可能會持續 使巴黎協定無法達成

乾旱和熱浪大幅增加了野火風險。有史以來野火造成的三次最大經濟損失都發生在最近四年。2019年和2020年夏季,北極地區發生了前所未有的野火。2019年6月,這些野火向大氣排放了5000萬噸二氧化碳,造成永凍土融化。2019年和2020年,亞馬遜雨林發生了大火,對環境造成了巨大影響。

2020科學界聯合報告引用的「世界天氣歸因」最近的一項研究結果指出,由於人為氣候變遷,2020年1月至2020年6月的高溫可能性至少高出600倍。

報告中記載的暖化趨勢很可能會持續下去,使全世界無法實現2015年巴黎協定設定的氣候目標,全球氣溫上升幅度遠低於工業化前水準2°C或僅比工業化高前1.5°C。

報告提供與氣候變遷相關的最新科學資料和發現,作為全球政策和行動的指引。內容聚焦氣候變遷的影響日益增加且不可逆轉,它影響冰川、海洋、自然、經濟和人類生活條件,人類往往可從乾旱或洪水等與水有關的危害切身感受到。

報告也記載了武漢肺炎如何破壞我們透過全球觀測系統監測這些變化的能力。

有史以來野火造成的三次最大經濟損失都發生在最近四年。照片來源: Ulet Ifansasti/Greenpeace(CC BY-NC-ND 2.0)

2020科學界聯合報告的主要發現

大氣中的溫室氣體濃度(世界氣象組織)

大氣中的二氧化碳濃度沒有要封頂的跡象,一直在打破紀錄。

根據WMO全球大氣監測網水準點的報告,2020年上半年二氧化碳濃度高於410 ppm,夏威夷冒納羅亞和澳洲塔斯馬尼亞格里姆角的觀測值分別為414.38 ppm和410.04 ppm。2020年比2019年7月增加了約3 ppm。

2020年二氧化碳排放量的減少只會輕微影響大氣中濃度的增加速度,因為今日大氣中二氧化碳濃度是過去和當前排放以及二氧化碳超長壽命所致結果。

WMO在其報告中表示:「要使氣候變遷穩定下來,必須將排放量持續減少至零淨值。」

全球化石燃料二氧化碳排放量(全球碳計畫)

由於武肺封鎖,2020年二氧化碳排放量預計將下降4%至7%。確切的下降百分比將取決於疫情控制狀況和政府因應方式。

2020年4月上旬的封城高峰期,全球每日化石燃料二氧化碳排放量與2019年相比下降了前所未有的17%。

但儘管如此,排放量仍與2006年的水準相當,凸顯過去15年來的急劇增長及長期依賴化石能源。

到2020年6月上旬,全球每日化石燃料排放量只比2019年水準低了5%不到,去年達到了367億公噸的新紀錄,比1990年氣候變遷談判開始時高62%。

過去10年間,人類活動產生的全球甲烷排放量也在持續增加。報告警告:「目前的二氧化碳和甲烷排放趨勢均無法達到巴黎協定目標。」

排放差距(聯合國環境規劃署)

聯合國環境規劃署呼籲,要實現巴黎協定目標,轉型行動不能再延。

環境署「2019年排放差距報告」顯示,從2020~2030年,要達到巴黎協定的2°C目標,每年要將全球排放量削減3%,要達到1.5°C目標平均每年要削減7%以上。

根據目前的預估,2030年與2°C目標的排放差距為120~150億公噸二氧化碳當量(CO2e),與1.5°C目標的排放差距為29~32吉噸二氧化碳當量,大約等於六個最大排放國的排放總量。

環境署說:「仍然有可能縮小這個排放差距,但需要所有國家和所有部門立即協調一致的行動……短期來說可以透過擴大現有的有效的政策來實現,例如再生能源和能源效率、低碳運輸以及逐步淘汰煤炭的政策。」

全球氣候狀況(WMO和英國氣象局)

2016~2020年的全球平均溫度將是有記錄以來最高溫,比前工業化時代參考期1850~1900年高出約1.1°C,比2011~2015年的全球平均溫度高出0.24°C。

在2020年至2024年這五年期間,至少有一年比工業化前水準高出1.5°C以上的機率是24%,五年平均值超過該水準的機會很小(3%)。兩家機構在報告中表示:「未來五年內,有70%的機率有一個或多個月的氣溫可能比工業化前高至少1.5°C。」

2016年至2020年間的每一年,北極海冰面積都低於平均水準。

2016至2019年的冰川質量損失均大於1950年以來的每個五年期。

2011至2015年和2016至2020年這兩個五年相比,全球平均海平面上升速度有所提高。

氣候變遷下的海洋和冰凍圈(政府間氣候變遷專門委員會)

人為氣候變遷正在影響從山頂到海洋深處的生命維持系統,導致海平面上升加快,對生態系統和人類安全產生連鎖反應,也對適應和綜合風險管理造成嚴峻挑戰。

全球的冰蓋和冰川正在消失。1979年至2018年間,一年之中每個月的北極海冰範圍都一直在減少。野火增加、永凍土突然融化以及北極和山區水文的變化,已經改變了生態系統擾動的頻率和強度。

1970年以來,全球海洋暖化不停息,並吸收了氣候系統90%以上的多餘熱量。自1993年以來,海洋暖化的速度和所吸收的來自氣候系統的熱量增加了一倍以上。

海洋熱浪的頻率增加了一倍,持續時間更長、強度更大、範圍更廣,導致大規模珊瑚白化。自1980年代以來,海洋吸收了人為二氧化碳總排放量的20%至30%,使海洋進一步酸化。

自大約1950年以來,海洋暖化、海冰變化和氧氣流失,許多海洋物種的分佈範圍和季節性活動發生了變化。

由於格陵蘭和南極冰蓋的冰流失率增加、冰川持續流失和海洋熱膨脹,近幾十年來海平面加速上升。2006至2015年全球平均海平面上升速度為每年3.6±0.5公釐,這在上個世紀前所未見。

全球的冰蓋和冰川正在消失。美國冰河灣國家公園。照片來源:mulf(CC BY-NC-ND 2.0)

氣候與水資源(WMO)

氣候變遷最明顯的影響出現在水文條件的變化,包括冰雪動力學的變化。

到2050年,受洪水威脅的人數將從目前的12億增加到16億。在2010年代初到中期,有19億人(全球人口的27%)生活在可能缺水的地區。到2050年,這個數字將增加到27~32億。

截至2019年,全世界有12%人口的飲用水來自未經改進和不安全的水源。全世界有30%以上的人口(即24億人)沒有任何形式的衛生設施。

氣候變遷將使更多地區缺水,已經缺水的地區將更嚴重。

冰凍圈是山區及其下游地區的重要淡水來源。學界認為,冰川的年徑流最晚將在21世紀末達到全球最高峰。此後全球冰川徑流將減少,影響水的儲存。

據估計,中歐和高加索地區現已達到最高水位,青藏高原地區將在2030年至2050年達到最高水位。隨著積雪融化形成徑流,該地區的永凍土和冰川佔河流總流量的45%,流量減少將影響17億人的用水。

武肺期間的地球系統觀測(教科文組織和WMO政府間海洋學委員會)

武漢肺炎嚴重影響全球觀測工作,進而影響預報以及其他天氣、氣候和海洋相關服務的品質。

3月和4月的飛行器觀測工作平均減少了75%至80%,降低了天氣模型的預報能力。自6月以來僅略有恢復。人工操作的氣象站觀測工作也受到嚴重干擾,尤其在非洲和南美。

諸如河流流量之類的水文觀測情況也類似。自動化系統可以繼續傳遞數據,而人工讀取的測量站受到影響。

2020年3月,幾乎所有海洋學研究船都被召回國籍港口。商用船無法提供重要的海洋和天氣觀測資料,並且無法維護海洋浮標和其他系統。每10年要進行四次的全深度海洋調查,包括碳、溫度、鹽度和水鹼度等變量偵測,都取消了。提供溫室氣體排放資訊的的船舶表面碳測量工作也幾乎停止。

這對氣候變遷監測的影響是長期的,可能會阻礙或限制融冰期結束時進行的冰川質量變化或永凍土厚度的測量活動。觀測活動中斷將使基本氣候變量的歷史時間序列產生斷層,不利監測氣候變動和變遷以及相關影響。

Climate Change Intensifies Despite Pandemic Lockdowns GENEVA, Switzerland, September 10, 2020 (ENS)

Already at their highest levels in three million years, greenhouse gas concentrations in the atmosphere continue to increase, lockdowns around the world to slow the spread of the pandemic coronavirus have forced vehicles to stay parked, making way for clearer skies – temporarily.

But climate change has not stopped for COVID-19. Emissions are heading in the direction of pre-pandemic levels following a temporary decline caused by the lockdown and economic slowdown.

In 2020, emissions of the greenhouse gas carbon dioxide (CO2) are projected to fall by an estimated four to seven percent due to COVID-19 confinement policies. The exact drop in atmospheric CO2 will depend on the trajectory of the pandemic and government responses to address it.

These facts are contained in a new multi-agency report from the world’s largest and most respected scientific organizations, “United in Science 2020.”

The report, the second in a series, was coordinated by the World Meteorological Organization, WMO, with input from the Global Carbon Project, the Intergovernmental Panel on Climate Change, the Intergovernmental Oceanographic Commission of UNESCO, the UN Environment Programme and the UK Met Office.

WMO Secretary-General Professor Petteri Taalas warned, “Greenhouse gas concentrations – which are already at their highest levels in three million years – have continued to rise. Meanwhile, large swathes of Siberia have seen a prolonged and remarkable heatwave during the first half of 2020, which would have been very unlikely without anthropogenic climate change. And now 2016–2020 is set to be the warmest five-year period on record.

“This report shows that whilst many aspects of our lives have been disrupted in 2020, climate change has continued unabated,” Taalas said.

“Major impacts have been caused by extreme weather and climate events. A clear fingerprint of human-induced climate change has been identified on many of these extreme events,” the WMO and UN Met Office say in the report.

Drought and heatwaves substantially increased the risk of wildfires. The three largest economic losses on record from wildfires have all occurred in the last four years. Summer 2019 and 2020 saw unprecedented wildfires in the Arctic region. In June 2019, these fires emitted 50 million tonnes of CO2 into the atmosphere and caused the loss of permafrost. In 2019 and 2020 there were also widespread fires in the Amazon rainforest, with dramatic environmental impacts.

The results of a recent study by World Weather Attribution cited in “United in Science 2020” showed with high confidence that the January to June 2020 heat is at least 600 times more likely as a result of human-induced climate change.

The warming trend documented in this report is likely to continue, and the world is not on track to meet targets set in the 2015 Paris Agreement on climate to keep the global temperature increase well below 2°C or at 1.5°C above pre-industrial levels.

“United in Science 2020” presents the latest scientific data and findings related to climate change to inform global policy and action. It highlights the increasing and irreversible impacts of climate change, which affects glaciers, oceans, nature, economies and human living conditions and is often felt through water-related hazards such as drought or flooding.

It also documents how COVID-19 has impeded our ability to monitor these changes through the global observing system.

“This has been an unprecedented year for people and planet. The COVID-19 pandemic has disrupted lives worldwide. At the same time, the heating of our planet and climate disruption has continued apace,” said UN Secretary-General António Guterres in a foreword to the report.

“Never before has it been so clear that we need long-term, inclusive, clean transitions to tackle the climate crisis and achieve sustainable development. We must turn the recovery from the pandemic into a real opportunity to build a better future,” said Guterres, who presented the report to the UN on Wednesday. “We need science, solidarity and solutions.”

KEY FINDINGS FROM “UNITED IN SCIENCE 2020”

Greenhouse Gas Concentrations in the Atmosphere (World Meteorological Organization)

Atmospheric CO2 concentrations showed no signs of peaking and have continued to increase to new records.

Benchmark stations in the WMO Global Atmosphere Watch network reported CO2 concentrations above 410 parts per million (ppm) during the first half of 2020, with observations from Mauna Loa, Hawaii and Cape Grim, Tasmania at 414.38 ppm and 410.04 ppm, respectively, in July 2020, up about three parts per million from July 2019.

Reductions in emissions of CO2 in 2020 will only slightly impact the rate of increase in the atmospheric concentrations, which are the result of past and current emissions, as well as the very long lifetime of CO2.

“Sustained reductions in emissions to net zero are necessary to stabilize climate change,” the WMO states in its report.

Global Fossil CO2 emissions (Global Carbon Project)

CO2 emissions in 2020 will fall by an estimated four percent to seven percent due to COVID-19 confinement policies. The exact percent of decline will depend on the trajectory of the pandemic and government responses to address it.

During peak lockdown in early April 2020, the daily global fossil CO2 emissions dropped by an unprecedented 17 percent compared to 2019.

But even so, emissions were still equivalent to 2006 levels, highlighting both the steep growth over the past 15 years and the continued dependence on fossil sources for energy.

By early June 2020, global daily fossil CO2 emissions had mostly returned to within five percent below 2019 levels, which reached a new record of 36.7 gigatonnes last year, 62 percent higher than at the start of climate change negotiations in 1990.

Global methane emissions from human activities, too, have continued to increase over the past decade. “Current emissions of both CO2 and methane are not compatible with emissions pathways consistent with the targets of the Paris Agreement,” the report warns.

Emissions Gap (UN Environment Programme)

“Transformational action can no longer be postponed if the Paris Agreement targets are to be met,” urges the UN Environment Programme.

The UNEP Emissions Gap Report 2019 showed that the cuts in global emissions required per year from 2020 to 2030 are close to three percent for a 2°C target and more than seven percent per year on average for the 1.5°C goal of the Paris Agreement.

The Emissions Gap in 2030 is estimated at 12-15 gigatonnes (Gt) of carbon dioxide equivalent (CO2e) to limit global warming to below 2°C. For the 1.5°C goal, the gap is estimated at 29-32 Gt CO2e, roughly equivalent to the combined emissions of the six largest-emitting countries.

“It is still possible to bridge the emissions gap, but this will require urgent and concerted action by all countries and across all sectors,” UNEP said.

“A substantial part of the short-term potential can be realized through scaling up existing, well-proven policies, for instance on renewables and energy efficiency, low carbon transportation means and a phase-out of coal,” the UN agency said.

Technically and economically feasible solutions already exist, said UNEP. Looking beyond the 2030 timeframe, new technological solutions and gradual change in consumption patterns are needed at all levels.

State of Global Climate (WMO and UK’s Met Office)

The average global temperature for 2016–2020 is expected to be the warmest on record, about 1.1°C above 1850-1900, a reference period for temperature change since pre-industrial times and 0.24°C warmer than the global average temperature for 2011-2015.

In the five-year period 2020–2024, the chance of at least one year exceeding 1.5°C above pre-industrial levels is 24 percent, with a very small chance (three percent) of the five-year mean exceeding this level. “It is likely (~70 percent chance) that one or more months during the next five years will be at least 1.5 °C warmer than pre-industrial levels,” the two agencies said in the report.

In every year between 2016 and 2020, the Arctic sea ice extent has been below average.

The years 2016–2019 recorded a greater glacier mass loss than all other past five-year periods since 1950.

The rate of global mean sea-level rise increased between the five-year periods 2011–2015 and 2016–2020.

The Ocean and Cryosphere in a Changing Climate (Intergovernmental Panel on Climate Change)

Human-induced climate change is affecting life-sustaining systems, from the top of the mountains to the depths of the oceans, leading to accelerating sea-level rise, with cascading effects for ecosystems and human security.

This increasingly challenges adaptation and integrated risk management responses.

Ice sheets and glaciers worldwide have lost mass. Between 1979 and 2018, Arctic sea-ice extent has decreased for all months of the year. Increasing wildfire and abrupt permafrost thaw, as well as changes in Arctic and mountain hydrology, have altered the frequency and intensity of ecosystem disturbances.

The global ocean has warmed unabated since 1970 and has taken up more than 90 percent of the excess heat in the climate system. Since 1993 the rate of ocean warming, and thus heat uptake has more than doubled.

Marine heatwaves have doubled in frequency and have become longer-lasting, more intense and more extensive, resulting in large-scale coral bleaching events. The ocean has absorbed between 20 percent to 30 percent of total anthropogenic CO2 emissions since the 1980s causing further ocean acidification.

Since about 1950 many marine species have undergone shifts in geographical range and seasonal activities in response to ocean warming, sea-ice change and oxygen loss.

The global mean sea-level is rising, with acceleration in recent decades due to increasing rates of ice loss from the Greenland and Antarctic ice sheets, as well as continued glacier mass loss and ocean thermal expansion. The rate of global mean sea-level rise for 2006–2015 of 3.6 ±0.5 mm/yr is unprecedented over the last century

Climate and Water Resources (WMO)

Climate change impacts are most felt through changing hydrological conditions including changes in snow and ice dynamics.

By 2050, the number of people at risk of floods will increase from its current level of 1.2 billion to 1.6 billion. In the early to mid-2010s, 1.9 billion people, or 27 percent of the global population, lived in potentially water-scarce areas. In 2050, this number will increase to 2.7 to 3.2 billion people.

As of 2019, 12 percent of the world population drinks water from unimproved and unsafe sources. More than 30 percent of the world population, or 2.4 billion people, live without any form of sanitation.

Climate change is projected to increase the number of water-stressed regions and exacerbate shortages in already water-stressed regions.

The cryosphere is an important source of freshwater in mountains and their downstream regions. There is high confidence that annual runoff from glaciers will reach peak globally at the latest by the end of the 21st century. After that, glacier runoff is projected to decline globally with implications for water storage.

It is estimated that Central Europe and Caucasus have reached peak water now and that the Tibetan Plateau region will reach peak water between 2030 and 2050. As runoff from snow cover, permafrost and glaciers in this region provides up to 45 percent of the total river flow, the flow decrease would affect water availability for 1.7 billion people.

Earth System Observations during COVID-19 (Intergovernmental Oceanographic Commission of UNESCO and WMO)

The COVID-19 pandemic has produced significant impacts on the global observing systems, which in turn have affected the quality of forecasts and other weather, climate and ocean-related services.

The reduction of aircraft-based observations by an average of 75 percent to 80 percent in March and April degraded the forecast skills of weather models. Since June, there has been only a slight recovery. Observations at manually operated weather stations, especially in Africa and South America, have also been badly disrupted.

For hydrological observations like river discharge, the situation is similar to that of atmospheric in situ measurements. Automated systems continue to deliver data whereas gauging stations that depend on manual reading are affected.

In March 2020, nearly all oceanographic research vessels were recalled to home ports. Commercial ships have been unable to contribute vital ocean and weather observations, and ocean buoys and other systems could not be maintained. Four full-depth ocean surveys of variables such as carbon, temperature, salinity, and water alkalinity, completed only once per decade, have been canceled. Surface carbon measurements from ships, which tell us about the evolution of greenhouse gases, also effectively ceased.

The impacts on climate change monitoring are long-term. They are likely to prevent or restrict measurement campaigns for the mass balance of glaciers or the thickness of permafrost, usually conducted at the end of the thawing period. The overall disruption of observations will introduce gaps in the historical time series of Essential Climate Variables needed to monitor climate variability and change and associated impacts.

※ 全文及圖片詳見:ENS

溫室氣體
冰川崩塌
熱浪
極端高溫
山林野火
極圈
疫情看氣候與能源
深度低碳專題
國際新聞
氣候變遷

作者

姜唯

如果有一件事是重要的,如果能為孩子實現一個願望,那就是人類與大自然和諧共存。

林大利

於特有生物研究保育中心服務,小鳥和棲地是主要的研究對象。是龜毛的讀者,認為龜毛是探索世界的美德。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

這款20來萬的熱銷7座SUV,不僅舒服而且空間賊大!

0L/2。5L最大馬力(pS):150/186最大扭矩(Nm):200/233變速箱:6MT/CVT百公里加速(s):11。7/10。5百公里油耗(L):7。1/7。7車主百公里油耗(L):8。81/9。58驅動方式:前置前驅/前置四驅底盤懸挂:前麥弗遜+橫向穩定桿/后複合多連桿+橫向穩定桿實際體驗(體驗者178cm):前排頭部1拳/後排頭部1拳/後排腿部2拳3指。

日產奇駿是一款的十分低調,但卻比較重要的車型。它沒有像全新換代的本田CR-V那樣以大量科技來收穫大眾目光,但是奇駿卻憑藉良好品質獲得了很高的銷量,在2017年它在合資緊湊型SUV銷量榜上排行第三!

奇駿車內乘空間很充足,而且還推出了7座車型,第三排座椅用來應急還不錯。而且它的座椅擁有着沙發般的柔軟承托,車身隔音水平不錯,駕駛過程中底盤濾振也頗為徹底,舒適性是相當突出的!

此外,奇駿還搭載一套性能突出的適時四驅系統,越野性能在城市SUV中是突出的。它採用多片離合器式限滑差速器進行前後輪扭矩配比,能夠達到前後各50%的動力配比,實用性不錯!

長寬高:4675*1830*1724mm

軸距:2706mm

定位:緊湊型SUV

在造型方面,奇駿使用了日產V-motion式前臉設計,鍍鉻裝飾件粗壯、中網格柵也比老款更大了,前臉的衝擊感挺強的。而車側和尾部造型依然厚重、飽滿,但是細節的亮點不太多。

內飾方面它採用了中央對稱式設計,並且提供了拼色的設計樣式,視覺上能給人不錯的舒適感。中控台採用了軟性材質包裹,車門的軟包也做得到位。方向盤採用了平底設計,設計更偏向年輕化,整體氛圍較好!

發動機:2.0L/2.5L

最大馬力(pS):150/186

最大扭矩(Nm):200/233

變速箱:6MT/CVT

百公里加速(s):11.7/10.5

百公里油耗(L):7.1/7.7

車主百公里油耗(L):8.81/9.58

驅動方式:前置前驅/前置四驅

底盤懸挂:前麥弗遜+橫向穩定桿/后複合多連桿+橫向穩定桿

實際體驗(體驗者178cm):前排頭部1拳/後排頭部1拳/後排腿部2拳3指。而第三排座椅腿部空間比較一般,適合緊急情況下使用。

感興趣的朋友可以點擊小程序查看詳細口碑,從口碑中可以看到車主們對奇駿的硬朗外形、通過性較好、後排乘坐空間大等優點頗為滿意,但是對腳剎駐車和內飾用料方面有些意見。

咱們發現奇駿幅度還是比較可觀的,在廣州需搭配店內上保險、貸款、上牌、置換等項目,而在北京、武漢、上海、成都等地則為現金優惠,性價比不低!

奇駿這款車型乘坐舒適性和行駛品質都有着不錯的表現!動力系統也省心耐用,是一款稱職的家用車,而且四驅系統也具備不錯的性能,優惠幅度也還不錯,所以銷量方面一直保持強勢。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

除了思域,這3款10來萬的轎車也能讓你爽到停不下來

但速度上去之後還是很有感覺的,特別是當你跑高速時,沉穩的底盤精準的轉向能給予你足夠的信心,可能不經意間你的時速就上百了,如果你覺得普通版的高爾夫不夠過癮,那就上GTI或R-Line吧,畢竟小鋼炮是不會浪得虛名的。說到這可能很多朋友糊塗了,操控都是它們的主旋律,那到底要選哪款呀。

最近不少網友諮詢想要一輛具有駕駛樂趣的緊湊型轎車該買哪款好,小雅腦中第一個冒出來的當然是思域啦,但是仔細想想思域太火了、等車實在太煎熬,所以今天我推薦另外三款,快來看看有沒有對你口味的:

一、外觀與內飾

如果你說思域是日繫緊湊型轎車的顏值擔當,馬自達車主可要表示不服了,瞧瞧這輛昂克賽拉,我保證在車流中你能一眼就找出來,魂動設計賦予了它獨特的韻律美,盾型進氣格柵、翼型鍍鉻飾條、犀利的前大燈、偏長的引擎蓋、再加上動感的線條,說不驚艷那還真是騙人的。

第二個要介紹的是福克斯,它有着一張略顯囂張的前臉,獠牙狀進氣格柵顯露出十足的攻擊性,鷹眼大燈看上去炯炯有神,車身線條力量感十足,果然很有運動氣息。

相比之下,小雅覺得高爾夫看起來要低調很多,沒有昂克賽拉那嫵媚多姿的車身線條,也沒有福克斯那搶眼的馬丁臉,可偏偏是這樣的大眾家族式設計風格依舊能討得不少人的喜愛,畢竟每個人的審美不一樣。

我們接着來看內飾:

昂克賽拉與福克斯的用料都中規中矩,不過昂克賽拉的中控面板上按鍵比較少,看起來十分簡潔,並且懸浮式的中控显示屏提升不少檔次感。福克斯的中控台佔用空間較多,讓我覺得有點壓迫感,並且做工要粗糙一些。

高爾夫的內飾設計是三車中最有質感的,雖然看起來並沒什麼新鮮味道,但它摸起來軟、做工細緻,而且中控面板的設計整體偏向駕駛席一側,方便駕駛員在行車時進行操作。

二、駕駛感受

關於造型設計就先說到這了,畢竟我們今天的主題是駕駛樂趣 ,那麼三者中誰的駕控最好呢?

昂克賽拉最大的優點主要有兩個,首先自然吸氣發動機動力輸出很線性,變速箱降擋平順,還有一點是轉向特別靈敏。雖然小雅覺得人車合一這個口號略顯誇張,但不可否認它開起來確實很靈活輕快。

對了,補充一點,昂克賽拉還加上了GVC扭矩分配系統,用於改善過彎時車輛的動態表現,集這麼多亮點於一身,我想這也是廣大車迷朋友們喜歡它的主要原因。

再來看看福克斯,這位運動健將近年來慢慢朝舒適性方向有所靠攏了。以前的福克斯動力很猛,還可謂是指哪打哪,但現在略偏舒適的調校讓它變得不那麼純粹了。

但它依舊是這個級別裏面不可忽視的,它的1.5T發動機功率在同排量渦輪增壓發動機中最出色的,最大馬力高達181匹,儘管油耗有點讓人心疼,但是它加速暢快啊。總的來說儘管整體質量口碑一般,但這並不影響年輕人將其作為人生第一部車的首選。

很多高爾夫車主被稱為“神車黨”,不過你要知道高爾夫有很多種,分為普通版、R-Line、GTI,動力的選擇也是多種多樣,其中的1.4T發動機匹配7擋雙離合變速箱可以說是黃金組合了,變速箱換擋快,動力的話初段加速不會太給力,需要你踩深一點油門。

但速度上去之後還是很有感覺的,特別是當你跑高速時,沉穩的底盤精準的轉向能給予你足夠的信心,可能不經意間你的時速就上百了,如果你覺得普通版的高爾夫不夠過癮,那就上GTI或R-Line吧,畢竟小鋼炮是不會浪得虛名的。

說到這可能很多朋友糊塗了,操控都是它們的主旋律,那到底要選哪款呀!別急,我們接着來看它們的配置:

三、裝備率

1、福克斯全系標配前後排頭部氣囊/氣簾,氣囊的重要性想必大家也都知道,所以說福克斯這點做的很厚道,而高爾夫要在指導價為14.49萬的2018款 1.6L 自動舒適型上才開始配備,昂克賽拉就更讓小雅失望了,只有頂配車型才配備有。

2、福克斯與昂克賽拉的頂配車型帶有主動安全系統,高爾夫全系沒有這一配置,而是多出疲勞駕駛提示功能,但小雅覺得有點華而不實。

3、再來看看倒車視頻影像,福克斯又給我了驚喜,做到了全系標配,昂克賽拉要在指導價為12.89萬的2017款 三廂 1.5L 手動豪華型才開始配備,而2018款的高爾夫只有GTI與280TSI 自動旗艦型上才帶有。

4、在燈光配置方面,昂克賽拉的LED大燈普及率高,在12.89萬的2017款 三廂 1.5L 手動豪華型上便可獲得,高爾夫要在15.79萬的2018款 280TSI 手動R-Line型才能買到,福克斯則全系都找不到LED大燈的影子。

5、另外,昂克賽拉全系標配多功能方向盤,福克斯全系標配8英寸中控屏,高爾夫全系支持全車車窗一鍵升降。

小雅覺得三車中福克斯的價格不算高,但配置的普及率卻非常高,再結合上面的表現來看是三車中性價比最高的。

四、結語

當然車無完車,三者最大的不足是空間都不太理想,畢竟這不是它們主攻的方面,另外高爾夫的價格比較高,特別是售價高達20多萬的GTI令不少人望而卻步。

但不管怎樣,手握住它們三中間任何一輛的方向盤,你都會忍不住多跑一會,難道不是嗎?本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

比思域還快,車主都說這款20來萬的SUV配置高動力強

發動機:1。5T/2。0T最大馬力(pS):181/245最大扭矩(Nm):240/350變速箱:6AT百公里加速(s):9。92/7。1百公里油耗(L):7。2/8。2車主百公里油耗(L):9。61/11。9驅動方式:前置前驅/前置四驅底盤懸挂:前麥弗遜/后多連桿前排腿部空間(mm):870-1085前排高度(mm):990前排寬度(mm):1470後排腿部空間(mm):610-850後排高度(mm):955後排寬度(mm):1485實際體驗(體驗者178cm):前排頭部1拳/後排頭部4指/後排腿部2拳

如果你手持20萬的購車預算,想買一款緊湊型SUV的的話,有非常多的選擇。而如果你對於操控性和動力有較大需求的話,福特的翼虎是一個不錯的選擇!

首先,翼虎懸架的調校帶有的韌性,它對於路面細碎振動的過濾徹底,提供了不錯的舒適性,同時在過彎時支撐性比較充足,尾部隨動性也不錯,駕駛起來頗為靈活!

它搭載的1.5T、2.0T兩台發動機具備強勁功率,與它們匹配的6AT變速箱降擋頗為积極,而且它帶有換擋撥片,可玩性挺高它還帶有S擋,能提升動力響應的靈敏程度。它2.0T四驅車型的破百時間僅為7.1秒,而1.5T兩驅車型百公里加速則在9.92秒內。

長寬高:4524*1838*1701mm

軸距:2690mm

定位:緊湊型SUV

外觀方面,現款的翼虎車身線條設計很凌厲,中網上粗壯的橫向條裝飾富有力量感。而且尾燈加入了黑色描邊細節,時尚感不低。而尾部的下方則採用了讓雙邊共兩出的排氣,運動感頗為出眾!

而內飾方面則為比較保守的設計風格,中控台運用軟質搪塑工藝,手感不錯。細節處加入了鋼琴漆黑色亮面裝飾條點綴,質感表現還不錯。

發動機:1.5T/2.0T

最大馬力(pS):181/245

最大扭矩(Nm):240/350

變速箱:6AT

百公里加速(s):9.92/7.1

百公里油耗(L):7.2/8.2

車主百公里油耗(L):9.61/11.9

驅動方式:前置前驅/前置四驅

底盤懸挂:前麥弗遜/后多連桿

前排腿部空間(mm):870-1085

前排高度(mm):990

前排寬度(mm):1470

後排腿部空間(mm):610-850

後排高度(mm):955

後排寬度(mm):1485

實際體驗(體驗者178cm):前排頭部1拳/後排頭部4指/後排腿部2拳2指。

感興趣的朋友可以點擊小程序查看詳細口碑,從口碑中可以看到車主們對翼虎的乘坐空間、操控性、2.0T發動機的強勁動力頗為滿意,但是對較高的油耗有些不滿。

咱們發現翼虎的優惠幅度中規中矩,在廣州地需搭配店內置換、貸款、上保險、上牌等項目,在北京、武漢地區的一些4S店還需加點裝飾。在成都地區則以現金優惠。

福特翼虎的電動助力轉向手感比較輕盈,精準度較高。懸架的調校帶有一定的韌性,它更多地側重於對震動的過濾,同時在過彎時支撐性比較充足,底盤質感較高!而且2.0T車型的動力表現強勁,當然油耗也較高!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

ThreadLocal源碼解析-Java8,ThreadLocal的使用場景分析,利用線性探測法解決hash衝突,Java-強引用、軟引用、弱引用、虛引用,利用線性探測法解決hash衝突,分析ThreadLocal的弱引用與內存泄漏問題-Java8

目錄

一.ThreadLocal介紹

  1.1 ThreadLocal的功能

  1.2 ThreadLocal使用示例

二.源碼分析-ThreadLocal

  2.1 ThreadLocal的類層級關係

  2.2 ThreadLocal的屬性字段

  2.3 創建ThreadLocal對象

  2.4 ThreadLocal-set操作

  2.5 ThreadLocal-get操作

  2.6 ThreadLocal-remove操作

三.ThreadLocalMap類

  3.0 線性探測算法解決hash衝突

  3.1 Entry內部類

  3.2 ThreadLocalMap的常量介紹

  3.3 實例化ThreadLocalMap

  3.4 ThreadLocalMap的set操作

  3.5 清理陳舊Entry和rehash

四.總結 

 

一.介紹ThreadLocal

1.1ThreadLocal的功能

  我們知道,變量從作用域範圍進行分類,可以分為“全局變量”、“局部變量”兩種:

  1.全局變量(global variable),比如類的靜態屬性(加static關鍵字),在類的整個生命周期都有效;

  2.局部變量(local variable),比如在一個方法中定義的變量,作用域只是在當前方法內,方法執行完畢后,變量就銷毀(釋放)了;

  使用全局變量,當多個線程同時修改靜態屬性,就容易出現併發問題,導致臟數據;而局部變量一般來說不會出現併發問題(在方法中開啟多線程併發修改局部變量,仍可能引起併發問題);

  再看ThreadLocal,可以用來保存局部變量,只不過這個“局部”是指“線程”作用域,也就是說,該變量在該線程的整個生命周期中有效。

  關於ThreadLocal的使用場景,可以查看ThreadLocal的使用場景分析。

 

1.2ThreadLocal的使用示例

  ThreadLocal使用非常簡單。

package cn.ganlixin;

import org.junit.Test;

import java.util.Arrays;
import java.util.List;

public class TestThreadLocal {

    private static class Goods {
        public Integer id;
        public List<String> tags;
    }

    @Test
    public void testReference() {
        Goods goods1 = new Goods();
        goods1.id = 10;
        goods1.tags = Arrays.asList("healthy", "cheap");

        ThreadLocal<Goods> threadLocal = new ThreadLocal<>();
        threadLocal.set(goods1);

        Goods goods2 = threadLocal.get();
        System.out.println(goods1); // cn.ganlixin.TestThreadLocal$Goods@1c655221
        System.out.println(goods2); // cn.ganlixin.TestThreadLocal$Goods@1c655221

        goods2.id = 100;
        System.out.println(goods1.id);  // 100
        System.out.println(goods2.id);  // 100

        threadLocal.remove();
        System.out.println(threadLocal.get()); // null
    }

    @Test
    public void test2() {
        // 一個線程中,可以創建多個ThreadLocal對象,多個ThreadLoca對象互不影響
        ThreadLocal<String> threadLocal1 = new ThreadLocal<>();
        ThreadLocal<String> threadLocal2 = new ThreadLocal<>();
        // ThreadLocal存的值默認為null

        System.out.println(threadLocal1.get()); // null

        threadLocal1.set("this is value1");
        threadLocal2.set("this is value2");
        System.out.println(threadLocal1.get()); // this is value1
        System.out.println(threadLocal2.get());  // this is value2

        // 可以重寫initialValue進行設置初始值
        ThreadLocal<String> threadLocal3 = new ThreadLocal<String>() {
            @Override
            protected String initialValue() {
                return "this is initial value";
            }
        };
        System.out.println(threadLocal3.get()); // this is initial value
    }
}

  

二.源碼分析-ThreadLocal

2.1ThreadLocal類層級關係

  

  ThreadLocal類中有一個內部類ThreadLocalMap,這個類特別重要,ThreadLocal的各種操作基本都是圍繞ThreadLocalMap進行的

  對於ThreadLocalMap有來說,它內部定義了一個Entry內部類,有一個table屬性,是一個Entry數組,和HashMap有一些相似的地方,但是ThreadLocalMap和HashMap並沒有什麼關係。

  先大概看一下內存關係圖,不理解也沒關係,看了後面的代碼應該就能理解了:

   

  大概解釋一下,棧中的Thread ref(引用)堆中的Thread對象,Thread對象有一個屬性threadlocals(ThreadLocalMap類型),這個Map中每一項(Entry)的value是ThreadLocal.set()的值,而Map的key則是ThreadLocal對象。

  下面在介紹源碼的時候,會從兩部分進行介紹,先介紹ThreadLocal的常用api,然後再介紹ThreadLocalMap,因為ThreadLocal的api內部其實都是在操作ThreadLocalMap,所以看源碼時一定要知道他們倆之間的關係

 

2.2ThreadLocal的屬性

  ThreadLocal有3個屬性,主要的功能就是生成ThreadLocal的hash值。

// threadLocalHashCode用來表示當前ThreadLocal對象的hashCode,通過計算獲得
private final int threadLocalHashCode = nextHashCode();

// 一個AtomicInteger類型的屬性,功能就是計數,各種操作都是原子性的,在併發時不會出現問題
private static AtomicInteger nextHashCode = new AtomicInteger();

// hash值的增量,不是隨便指定的,被稱為“黃金分割數”,能讓hash結果均衡分佈
private static final int HASH_INCREMENT = 0x61c88647;

/**
 * 通過計算,為當前ThreadLocal對象生成一個HashCode
 */
private static int nextHashCode() {
    // 獲取當前nextHashCode,然後遞增HASH_INCREMENT
    return nextHashCode.getAndAdd(HASH_INCREMENT);
}

  

2.3創建ThreadLocal對象

  ThreadLocal類,只有一個無參構造器,如果需要是指默認值,則可以重寫initialValue方法:

public ThreadLocal() {}

/**
 * 初始值默認為null,要設置初始值,只需要設置為方法返回值即可
 *
 * @return ThreadLocal的初始值
 */
protected T initialValue() {
    return null;
}

  需要注意的是initialValue方法並不會在創建ThreadLocal對象的時候設置初始值,而是延遲執行:當ThreadLocal直接調用get時才會觸發initialValue執行(get之前沒有調用set來設置過值),initialValue方法在後面還會介紹。 

 

2.4ThreadLocal-set操作

  下面這段代碼只給出了ThreadLocal的set代碼:

public void set(T value) {
    // 獲取當前線程
    Thread t = Thread.currentThread();

    // 獲取當前線程的ThreadLocalMap屬性,ThreadLocal有一個threadLocals屬性(ThreadLocalMap類型)
    ThreadLocalMap map = getMap(t);

    if (map != null) {
        // 如果當前線程有關聯的ThreadLocalMap對象,則調用ThreadLocalMap的set方法進行設置
        map.set(this, value);
    } else {
        // 創建一個與當前線程關聯的ThreadLocalMap對象,並設置對應的value
        createMap(t, value);
    }
}

/**
 * 獲取線程關聯的ThreadLocalMap對象
 */
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

/**
 * 創建ThreadLocalMap
 * @param t          key為當前線程
 * @param firstValue value為ThreadLocal.set的值
 */
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

  如果想立即了解ThreadLocalMap的set方法,則可點此跳轉!

 

2.5ThreadLocal-get操作

  前面說過“重寫ThreadLocal的initialValue方法來設置ThreadLocal的默認值,並不是在創建ThreadLocal的時候執行的,而是在直接get的時候執行的”,看了下面的代碼,就知道這句話的具體含義了,感覺設計很巧妙:

public T get() {
    // 獲取當前線程
    Thread t = Thread.currentThread();

    // 獲取當前線程對象的threadLocals屬性
    ThreadLocalMap map = getMap(t);

    // 若當前線程對象的threadLocals屬性不為空(map不為空)
    if (map != null) {
        // 當前ThreadLocal對象作為key,獲取ThreadLocalMap中對應的Entry
        ThreadLocalMap.Entry e = map.getEntry(this);

        // 如果找到對應的Entry,則證明該線程的該ThreadLocal有值,返回值即可
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T) e.value;
            return result;
        }
    }

    // 1.當前線程對象的threadLocals屬性為空(map為空)
    // 2.或者map不為空,但是未在map中查詢到以該ThreadLocal對象為key對應的entry
    // 這兩種情況,都會進行設置初始值,並將初始值返回
    return setInitialValue();
}

/**
 * 設置ThreadLocal初始值
 *
 * @return 初始值
 */
private T setInitialValue() {
    // 調用initialValue方法,該方法可以在創建ThreadLocal的時候重寫
    T value = initialValue();
    Thread t = Thread.currentThread();

    // 獲取當前線程的threadLocals屬性(map)
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        // threadLocals屬性值不為空,則進行調用ThreadLocalMap的set方法
        map.set(this, value);
    } else {
        // 沒有關聯的threadLocals,則創建ThreadLocalMap,並在map中新增一個Entry
        createMap(t, value);
    }

    // 返回初始值
    return value;
}

/**
 * 初始值默認為null,要設置初始值,只需要設置為方法返回值即可
 * 創建ThreadLocal設置默認值,可以覆蓋initialValue方法,initialValue方法不是在創建ThreadLocal時執行,而是這個時候執行
 *
 * @return ThreadLocal的初始值
 */
protected T initialValue() {
    return null;
}

     

2.6ThreadLocal-remove操作

  一般是在ThreadLocal對象使用完后,調用ThreadLocal的remove方法,在一定程度上,可以避免內存泄露;

 

/**
 * 刪除當前線程中threadLocals屬性(map)中的Entry(以當前ThreadLocal為key的)
 */
public void remove() {
    // 獲取當前線程的threadLocals屬性(ThreadLocalMap)
    ThreadLocalMap m = getMap(Thread.currentThread());

    if (m != null) {
        // 調用ThreadLocalMap的remove方法,刪除map中以當前ThreadLocal為key的entry
        m.remove(this);
    }
}

 

三.ThreadLocalMap內部類

3.0 線性探測算法解決hash衝突

  在介紹ThreadLocalMap的之前,強烈建議先了解一下線性探測算法,這是一種解決Hash衝突的方案,如果不了解這個算法就去看ThreadLocalMap的源碼就會非常吃力,會感到莫名其妙。

  鏈接在此:利用線性探測法解決hash衝突

 

3.1Entry內部類

  ThreadLocalMap是ThreadLocal的內部類,ThreadLocalMap底層使用數組實現,每一個數組的元素都是Entry類型(在ThreadLocalMap中定義的),源碼如下:

/**
 * ThreadLocalMap中存放的元素類型,繼承了弱引用類
 */
static class Entry extends WeakReference<ThreadLocal<?>> {
    // key對應的value,注意key是ThreadLocal類型
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

  ThreadLocalMap和HashMap類似,比較一下:

  a:底層都是使用數組實現,數組元素類型都是內部定義,Java8中,HashMap的元素是Node類型(或者TreeNode類型),ThreadLocalMap中的元素類型是Entry類型;

  b.都是通過計算得到一個值,將這個值與數組的長度(容量)進行與操作,確定Entry應該放到哪個位置;

  c.都有初始容量、負載因子,超過擴容閾值將會觸發擴容;但是HashMap的初始容量、負載因子是可以更改的,而ThreadLocalMap的初始容量和負載因子不可修改;

  注意Entry繼承自WeakReference類,在實例化Entry時,將接收的key傳給父類構造器(也就是WeakReference的構造器),WeakReference構造器又將key傳給它的父類構造器(Reference):

// 創建Reference對象,接受一個引用
Reference(T referent) {
    this(referent, null);
}

// 設置引用
Reference(T referent, ReferenceQueue<? super T> queue) {
    this.referent = referent;
    this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}

  關於Java的各種引用,可以參考:Java-強引用、軟引用、弱引用、虛引用

 

3.2ThreadLocalMap的常量介紹

// ThreadLocalMap的初始容量
private static final int INITIAL_CAPACITY = 16;

// ThreadLocalMap底層存數據的數組
private Entry[] table;

// ThreadLocalMap中元素的個數
private int size = 0;

// 擴容閾值,當size達到閾值時會觸發擴容(loadFactor=2/3;newCapacity=2*oldCapacity)
private int threshold; // Default to 0

  

3.3創建ThreadLocalMap對象

  創建ThreadLocalMap,是在第一次調用ThreadLocal的set或者get方法時執行,其中第一次未set值,直接調用get時,就會利用ThreadLocal的初始值來創建ThreadLocalMap。

  ThreadLocalMap內部類的源碼如下:

/**
 * 初始化一個ThreadLocalMap對象(第一次調用ThreadLocal的set方法時創建),傳入ThreadLocal對象和對應的value
 */
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    // 創建一個Entry數組,容量為16(默認)
    table = new Entry[INITIAL_CAPACITY];

    // 計算新增的元素,應該放到數組的哪個位置,根據ThreadLocal的hash值與初始容量進行"與"操作
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);

    // 創建一個Entry,設置key和value,注意Entry中沒有key屬性,key屬性是傳給Entry的父類WeakReference
    table[i] = new Entry(firstKey, firstValue);

    // 初始容量為1
    size = 1;

    // 設置擴容閾值
    setThreshold(INITIAL_CAPACITY);
}

/**
 * 設置擴容閾值,接收容量值,負載因子固定為2/3
 */
private void setThreshold(int len) {
    threshold = len * 2 / 3;
}

 

3.4 ThreadLocalMap的set操作

  ThreadLocal的set方法,其實核心就是調用ThreadLocalMap的set方法,set方法的流程比較長

/**
 * 為當前ThreadLocal對象設置value
 */
private void set(ThreadLocal<?> key, Object value) {
    Entry[] tab = table;
    int len = tab.length;

    // 計算新元素應該放到哪個位置(這個位置不一定是最終存放的位置,因為可能會出現hash衝突)
    int i = key.threadLocalHashCode & (len - 1);

    // 判斷計算出來的位置是否被佔用,如果被佔用,則需要找出應該存放的位置
    for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
        // 獲取Entry中key,也就是弱引用的對象
        ThreadLocal<?> k = e.get();

        // 判斷key是否相等(判斷弱引用的是否為同一個ThreadLocal對象)如果是,則進行覆蓋
        if (k == key) {
            e.value = value;
            return;
        }

        // k為null,也就是Entry的key已經被回收了,當前的Entry是一個陳舊的元素(stale entry)
        if (k == null) {
            // 用新元素替換掉陳舊元素,同時也會清理其他陳舊元素,防止內存泄露
            replaceStaleEntry(key, value, i);
            return;
        }
    }

    // map中沒有ThreadLocal對應的key,或者說沒有找到陳舊的Entry,則創建一個新的Entry,放入數組中
    tab[i] = new Entry(key, value);
    // ThreadLocalMap的元素數量加1
    int sz = ++size;

    // 先清理map中key為null的Entry元素,該Entry也應該被回收掉,防止內存泄露
    // 如果清理出陳舊的Entry,那麼就判斷是否需要擴容,如果需要的話,則進行rehash
    if (!cleanSomeSlots(i, sz) && sz >= threshold) {
        rehash();
    }
}

  上面最後幾行代碼涉及到清理陳舊Entry和rehash,這兩塊的代碼在下面。

 

3.5清理陳舊Entry和rehash

  陳舊的Entry,是指Entry的key為null,這種情況下,該Entry是不可訪問的,但是卻不會被回收,為了避免出現內存泄漏,所以需要在每次get、set、replace時,進行清理陳舊的Entry,下面只給出一部分代碼:

/**
 * 清理map中key為null的Entry元素,該Entry也應該被回收掉,防止內存泄露
 *
 * @param i 新Entry插入的位置
 * @param n 數組中元素的數量
 * @return 是否有陳舊的entry的清除
 */
private boolean cleanSomeSlots(int i, int n) {
    boolean removed = false;
    Entry[] tab = table;
    int len = tab.length;
    do {
        i = nextIndex(i, len);
        Entry e = tab[i];
        if (e != null && e.get() == null) {
            n = len;
            removed = true;
            i = expungeStaleEntry(i);
        }
    } while ((n >>>= 1) != 0);
    return removed;
}

private void rehash() {
    // 清除底層數組中所有陳舊的(stale)的Entry,也就是key為null的Entry
    // 同時每清除一個Entry,就對其後面的Entry重新計算hash,獲取新位置,使用線性探測法,重新確定最終位置
    expungeStaleEntries();

    // 清理完陳舊Entry后,判斷是否需要擴容
    if (size >= threshold - threshold / 4) {
        // 擴容時,容量變為舊容量的2倍,再進行rehash,並使用線性探測發確定Entry的新位置
        resize();
    }
}

  在rehash的時候,涉及到“線性探測法”,是一種用來解決hash衝突的方案,可以查看利用線性探測法解決hash衝突了解詳情。

 

3.6ThreadLocalMap-remove操作

  remove操作,是調用ThreadLocal.remove()方法時,刪除當前線程的ThreadLocalMap中該ThreadLocal為key的Entry。

/**
 * 移除當前線程的threadLocals屬性中key為ThreadLocal的Entry
 *
 * @param key 要移除的Entry的key(ThreadLocal對象)
 */
private void remove(ThreadLocal<?> key) {
    Entry[] tab = table;
    int len = tab.length;

    // 計算出該ThreadLocal對應的key應該存放的位置
    int i = key.threadLocalHashCode & (len - 1);

    // 找到指定位置,開始按照線性探測算法進行查找到該Thread的Entry
    for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {

        // 如果Entry的key相同
        if (e.get() == key) {
            // 調用WeakReference的clear方法,Entry的key是弱引用,指向ThreadLocal,現在將key指向null
            // 則該ThreadLocal對象在會在下一次gc時,被垃圾收集器回收
            e.clear();

            // 將該位置的Entry中的value置為null,於是value引用的對象也會被垃圾收集器回收(不會造成內存泄漏)
            // 同時內部會調整Entry的順序(開放探測算法的特點,刪除元素後會重新調整順序)
            expungeStaleEntry(i);

            return;
        }
    }
}

 

四.總結

  在學習ThreadLocal類源碼的過程還是受益頗多的:

  1.ThreadLocal的使用場景;

  2.initialValue的延遲執行;

  3.HashMap使用鏈表+紅黑樹解決hash衝突,ThreadLocalMap使用線性探測算法(開放尋址)解決hash衝突

  另外,ThreadLocal還有一部分內容,是關於弱引用和內存泄漏的問題,可以參考:分析ThreadLocal的弱引用與內存泄漏問題-Java8。

 

  原文地址:https://www.cnblogs.com/-beyond/p/13093032.html

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?