疫情成保育契機 威尼斯潟湖重生

摘錄自2020年08月16日台灣醒報報導

疫情使威尼斯觀光停擺,在地人趁機復甦生態!義大利威尼斯潟湖曾因為人口眾多及排水污染,湖中鹽分提高,導致大量蘆葦消失,生態環境被破壞。然而,因為疫情期間人口稀少,科學家及當地農夫藉此發起潟湖復甦計劃,以運河系統引進淡水、移植海草、種植蘆葦,以恢復原有生態。

鹽沼是沿海潮間帶和陸地間的一種生態系統,海水或鹹水會規律地湧入流出該地區。據《今日泰倫加納》報導,人工的運河系統將思樂河的淡水導入潟湖。能培養豐富生態的鹽沼如今只剩下34公頃。

威尼斯福斯卡里宮大學研究員亞瑞安娜指出,潟湖的一半以上曾經是蘆葦床和鹽沼,約1萬7000公頃並擁有豐富生態,「健康的潟湖鹽分應介於0到15鹽度,但現在威尼斯的鹽度是30,已與海水相去不遠。」計劃發起人布魯薩則表示:「我們的重整計劃將從其他地方導入乾淨的淡水,來沖淡鹽分,還它原始的樣貌。」

污染治理
國際新聞
義大利
威尼斯
潟湖
武漢肺炎
動物與大環境變遷

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

【其他文章推薦】

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

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

德國國家科學院聲明 德國能源轉型2030:歐洲碳中和路徑

轉載自台大風險社會與政策研究中心;陳喬琪 編譯

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

【其他文章推薦】

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

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

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

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

強風助長 希臘科孚島森林野火延燒

摘錄自2020年8月17日中央社報導

希臘不少有錢人置產的科孚島(Corfu)一座松樹林今(17日)發生大火,強風助長下,火勢在東北沿岸持續延燒。

法新社報導,野火爆發點鄰近艾米提斯區(Erimitis)樹木林立的海濱城鎮阿吉歐史芬尼(Agios Stefanos),而當地名聲最顯赫的居民莫過於羅斯契德(Rothschild)家族及阿涅利(Agnelli)家族。

希臘當局表示,消防人員出動12輛消防車,並在飛機支援下力阻野火在這個英國媒體譽為「海上肯辛頓」(Kensington-on-sea)的地區蔓延。

希臘夏季溫度動輒超過攝氏30度,時常傳出野火。2018年7月,雅典東北濱海度假城鎮瑪蒂(Mati)的一場火勢造成102人喪生,為希臘近代史最嚴重火災。

土地利用
國際新聞
希臘
森林野火

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

【其他文章推薦】

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

※別再煩惱如何寫文案,掌握八大原則!

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

※超省錢租車方案

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

※產品缺大量曝光嗎?你需要的是一流包裝設計!

百公里時速「火龍捲」襲擊北加州 美西迎70年來最嚴重熱浪

摘錄自2020年8月17日東森新聞報導

美國國家氣象局(National Weather Service)15日針對北加州發布龍捲風警報,而且這次是稀有的火龍捲警報,由加州洛亞爾頓的野火火源的高溫引起熱空氣與濃煙上升變成「火積雲」(pyrocumulonimbus),形成時速近100公里的火龍捲。

駭人的火龍捲15日在內華達州邊境被發現,是當空氣中的漩渦亂流因為高熱及風向造成的湍流結合而形成,在旋風內有火焰。當這些渦旋空氣繼續收緊至類似龍捲風的結構時,可以吸入燃燒中的碎塊雜物及可燃氣體,從而使旋風點起火焰。龍捲風的存在可以幫助野火更快地傳播。

美國西部各州面臨70年來最嚴重的熱浪侵襲,超過800萬人收到政府的高溫警報,沙加緬度甚至預計達到攝氏43度。加州14日甚至因為溫度過高,電網不堪負荷,導致全州陷入停電。

氣候變遷
國際新聞
美國
熱浪
野火
龍捲風

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

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

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

※超省錢租車方案

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

網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!

越南貓肉市場猖獗 估百萬貓遭吃下肚

摘錄自2020年8月18日公視報導

根據國際動保團體的最新調查,過去只在越南北部較為普遍的貓肉餐廳,現在已經擴展到全國各地,每年大約會吃掉100萬隻貓,而越南政府從1998年以來的吃貓肉禁令,已經在今年1月廢除,動保團體擔心情況將會雪上加霜。

越南人在二戰後民不聊生,經常抓貓狗充飢,雖然政府在1998年曾明令禁止,但民間吃貓肉的風氣仍舊存在。有老一輩的越南人認為,月初吃貓會獲得好運,避免遭遇不幸,還有的認為經常吃貓肉可以像貓一樣敏捷;而有的餐廳將貓肉稱作老虎寶寶,或小老虎之類的,讓人相信吃了可以強身壯陽。

今年1月間越南政府廢除吃貓禁令後,動保團體發現,越南各地貓肉餐廳頓時多了起來,遍及會安、胡志明市等地,而距離河內兩個小時車程的太平省,就是貓隻屠宰場的大本營,整個屠宰過程極不人道。

在揭發業者抓捕屠殺貓隻的殘忍行徑後,動保團體希望越南民眾別再吃貓肉。

生物多樣性
國際新聞
越南

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

「氣候倡議者」賀錦麗出任拜登副手 智庫:絕對有利氣候外交

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

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

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

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

※產品缺大量曝光嗎?你需要的是一流包裝設計!

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

Cypress系列(18)- 可操作類型的命令 之 點擊命令

如果想從頭學起Cypress,可以看下面的系列文章哦

https://www.cnblogs.com/poloyy/category/1768839.html

 

前言

  • 啥是可操作類型?就是可以和 DOM 元素交互的命令,比如:點擊,雙擊…..等等等
  • 這些命令模擬用戶和應用程序交互,Cypress 會觸發瀏覽器事件,進而觸發應用程序綁定的時間

這一篇着重講點擊操作,一共有三個命令

  1. click:單擊
  2. dbclick:雙擊
  3. rightclick:右鍵

 

.click() 的語法和用法

單擊某個元素

 

六種基礎語法格式

// 單擊某個元素
.click()

// 帶參數的單擊
.click(options)

// 在某個位置點擊
.click(position)

// 在某個位置點擊,且帶參數
.click(position, options)

// 根據頁面坐標點擊
.click(x, y)

// 根據頁面坐標點擊,且帶參數
.click(x, y, options)

 

正確用法

宗旨:先獲取 DOM 元素,再對 DOM 元素操作

 

錯誤用法

 

position 位置參數

每個元素都有九個 position,具體可看下圖

 

坐標 x, y

距離 DOM 元素左上角的坐標,x 是橫軸,y 是豎軸

 

options 可選參數

共有四個

 

 

如何傳 options ?

  •  .click({ multiple: true }) 
  •  .click({ multiple: true , force: true}) 

 

force: true 的作用

背景

  • Cypress 可以通過 Test Runner 的快照找到阻止 DOM 元素交互的情況,但某些情況下可能會阻礙測試的進行
  • 比如:有一個嵌套的導航結構,用戶必須將鼠標 hover 在一個非常特定的模式中,才能拿到所需的鏈接
  • 當測試時,其實我們只是想獲取鏈接而已,前面過多的繁瑣操作可能會導致測試失敗

 

作用

  • 當設置了 force: true 時,Cypress 會強制操作命令的發生,避開前面的所有檢查
  • 你可以傳遞 { force: true } 給大多數操作命令

 

栗子

// 強制點擊,和所有後續事件
// 即使該元素 “不可操作”,也會觸發點擊操作
cy.get('button').click({ force: true })

 

當使用 force 時,將執行這些操作

  • 繼續執行所有默認操作
  • 強制在元素上觸發事件

 

當使用 force 時,將不會執行這些操作

  • 滾動到視圖中
  • 確保可見
  • 確保未禁用
  • 確保沒有分離
  • 確保它不是只讀的
  • 確保它沒有動畫
  • 確保未覆蓋
  • 向後代觸發事件

 

總結

總而言之, { force: true } 跳過檢查,它將始終在所需元素處觸發事件

 

.click() 具體的栗子

.click() 的栗子

測試文件代碼

 

 

測試結果

 

.click(position)

測試文件代碼

 

測試結果

 

.click(x, y)

測試文件代碼

 

測試結果

 

{force: true} 的栗子

.click(options)

 

.click(position, options)

 

.click(x, y, options)

 

{multiple : true } 的栗子

測試文件代碼

 

測試結果

 cy.get(‘ ul > li ‘) 共匹配四個 DOM 元素,他們均觸發單擊操作

 

單擊組合鍵

 .click() 命令還可以與 .type() 命令結合使用修飾符來觸發組合鍵操作,以便在單擊時結合鍵盤操作,例如ALT + click

 

以下修飾符可以和 .click() 結合使用

修飾符 作用 別名
{alt}
等價於 alt 鍵 {option}
{ctrl} 等價於 ctrl 鍵 {control}
{shift} 等價於 shift 鍵  

 

栗子

 

.dblclick()

雙擊,跟 click() 的語法 & 用法一致,只是變成了雙擊

cy.get("#main1").dblclick()
cy.get("#main1").dblclick("top")
cy.get("#main1").dblclick(15, 15)

 

.rightclick()

右鍵,跟 click() 的語法 & 用法一致,只是變成了右鍵點擊

cy.get("#li1").rightclick()
cy.get("#li1").rightclick("top")
cy.get("#li1").rightclick(15, 15)

 

.click() 注意事項

可操作性

執行 .click()  必須是 DOM 元素達到了可操作狀態

 

關於斷言

 .click() 將自動等待元素達到可操作狀態。

 .click() 將自動等待後面鏈接的斷言通過

 

超時時間

 .click() 如果 DOM 元素一直達不到可操作狀態,可能會超時

 .click() 如果後面鏈接的斷言一直不通過,可能會超時

 

.click() 會觸發的鼠標事件

在命令日誌中單擊 click 時,控制台console 將輸出以下鼠標事件

 

結尾

本文是博主基於對蔡超老師的《Cypress 從入門到精通》閱讀理解完后輸出的博文,並附上了自己的理解

對書籍感興趣的,大家可以參考本篇博客:https://www.cnblogs.com/poloyy/p/13052972.html,考慮自身需求進行購買

 

我的博客即將同步至騰訊雲+社區,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=12vd92hxgwgj1

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

【其他文章推薦】

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

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

【大廠面試07期】說一說你對synchronized鎖的理解?

synchronized鎖的原理也是大廠面試中經常會涉及的問題,本文主要通過對以下問題進行分析講解,來幫助大家理解synchronized鎖的原理。

1.synchronized鎖是什麼?鎖的對象是什麼?

2.偏向鎖,輕量級鎖,重量級鎖的執行流程是怎樣的?

3.為什麼說是輕量級,重量級鎖是不公平的?

4.重量級鎖為什麼需要自旋操作?

5.什麼時候會發生鎖升級,鎖降級?

6.偏向鎖,輕量鎖,重量鎖的適用場景,優缺點是什麼?

1.synchronized鎖是什麼?鎖的對象是什麼?

synchronized的英文意思就是同步的意思,就是可以讓synchronized修飾的方法,代碼塊,每次只能有一個線程在執行,以此來實現數據的安全。

一般可以修飾同步代碼塊、實例方法、靜態方法,加鎖對象分別為同步代碼塊塊括號內的對象、實例對象、類。

在實現原理上,

  • synchronized修飾同步代碼塊,javac在編譯時,在synchronized同步塊的進入的指令前和退出的指令后,會分別生成對應的monitorenter和monitorexit指令進行對應,代表嘗試獲取鎖和釋放鎖。
    (為了保證拋異常的情況下也能釋放鎖,所以javac為同步代碼塊添加了一個隱式的try-finally,在finally中會調用monitorexit命令釋放鎖。)
  • synchronized修飾方法,javac為方法的flags屬性添加了一個ACC_SYNCHRONIZED關鍵字,在JVM進行方法調用時,發現調用的方法被ACC_SYNCHRONIZED修飾,則會先嘗試獲得鎖。
public class SyncTest {
    private Object lockObject = new Object();
    public void syncBlock(){
        //修飾代碼塊,加鎖對象為lockObject
        synchronized (lockObject){
            System.out.println("hello block");
        }
    }
    //修飾實例方法,加鎖對象為當前的實例對象
    public synchronized void syncMethod(){
        System.out.println("hello method");
    }
    //修飾靜態方法,加鎖對象為當前的類
    public static synchronized void staticSyncMethod(){
        System.out.println("hello method");
    }
}

2.偏向鎖,輕量級鎖,重量級鎖的執行流程是怎樣的?

在JVM中,一個Java對象其實由對象頭+實例數據+對齊填充三部分組成,而對象頭主要包含Mark Word+指向對象所屬的類的指針組成(如果是數組對象,還會包含長度)。像下圖一樣:

Mark Word:存儲對象自身的運行時數據,例如hashCode,GC分代年齡,鎖狀態標誌,線程持有的鎖等等。在32位系統佔4字節,在64位系統中佔8字節,所以它能存儲的數據量是有限的,所以主要通過設立是否偏向鎖的標誌位鎖標誌位用於區分其他位數存儲的數據是什麼,具體請看下圖:

鎖信息都是存在鎖對象的Mark Word中的,當對象狀態為偏向鎖時,Mark Word存儲的是偏向的線程ID;當狀態為輕量級鎖時,Mark Word存儲的是指向線程棧中Lock Record的指針;當狀態為重量級鎖時,Mark Word為指向堆中的monitor對象的指針。

這是網上找到的一個流程圖,可以先看流程圖,結合著文字來了解執行流程

偏向鎖

Hotspot的作者經過以往的研究發現大多數情況下鎖不僅不存在多線程競爭,而且總是由同一線程多次獲得,於是引入了偏向鎖。

簡單的來說,就是主要鎖處於偏向鎖狀態時,會在Mark Word中存當前持有偏向鎖的線程ID,如果獲取鎖的線程ID與它一致就說明是同一個線程,可以直接執行,不用像輕量級鎖那樣執行CAS操作來加鎖和解鎖。

偏向鎖的加鎖過程:

場景一:當鎖對象第一次被線程獲得鎖的時候

線程發現是匿名偏向狀態(也就是鎖對象的Mark Word沒有存儲線程ID),則會用CAS指令,將mark word中的thread id由0改成當前線程Id。如果成功,則代表獲得了偏向鎖,繼續執行同步塊中的代碼。否則,將偏向鎖撤銷,升級為輕量級鎖。

場景二:當獲取偏向鎖的線程再次進入同步塊時

發現鎖對象存儲的線程ID就是當前線程的ID,會往當前線程的棧中添加一條Displaced Mark Word為空的Lock Record中,然後繼續執行同步塊的代碼,因為操縱的是線程私有的棧,因此不需要用到CAS指令;由此可見偏向鎖模式下,當被偏向的線程再次嘗試獲得鎖時,僅僅進行幾個簡單的操作就可以了,在這種情況下,synchronized關鍵字帶來的性能開銷基本可以忽略。

場景二:當沒有獲得鎖的線程進入同步塊時

當沒有獲得鎖的線程進入同步塊時,發現當前是偏向鎖狀態,並且存儲的是其他線程ID(也就是其他線程正在持有偏向鎖),則會進入到撤銷偏向鎖的邏輯里,一般來說,會在safepoint中去查看偏向的線程是否還存活

  • 如果線程存活且還在同步塊中執行,
    則將鎖升級為輕量級鎖,原偏向的線程繼續擁有鎖,只不過持有的是輕量級鎖,繼續執行代碼塊,執行完之後按照輕量級鎖的解鎖方式進行解鎖,而其他線程則進行自旋,嘗試獲得輕量級鎖。
  • 如果偏向的線程已經不存活或者不在同步塊中,
    則將對象頭的mark word改為無鎖狀態(unlocked)

由此可見,偏向鎖升級的時機為:當一個線程獲得了偏向鎖,在執行時,只要有另一個線程嘗試獲得偏向鎖,並且當前持有偏向鎖的線程還在同步塊中執行,則該偏向鎖就會升級成輕量級鎖。

偏向鎖的解鎖過程

因此偏向鎖的解鎖很簡單,其僅僅將線程的棧中的最近一條lock recordobj字段設置為null。需要注意的是,偏向鎖的解鎖步驟中並不會修改鎖對象Mark Word中的thread id,簡單的說就是鎖對象處於偏向鎖時,Mark Word中的thread id 可能是正在執行同步塊的線程的id,也可能是上次執行完已經釋放偏向鎖的thread id,主要是為了上次持有偏向鎖的這個線程在下次執行同步塊時,判斷Mark Word中的thread id相同就可以直接執行,而不用通過CAS操作去將自己的thread id設置到鎖對象Mark Word中。
這是偏向鎖執行的大概流程:

輕量級鎖

重量級鎖依賴於底層的操作系統的Mutex Lock來實現的,但是由於使用Mutex Lock需要將當前線程掛起並從用戶態切換到內核態來執行,這種切換的代價是非常昂貴的,而在大部分時候可能並沒有多線程競爭,只是這段時間是線程A執行同步塊,另外一段時間是線程B來執行同步塊,僅僅是多線程交替執行,並不是同時執行,也沒有競爭,如果採用重量級鎖效率比較低。以及在重量級鎖中,沒有獲得鎖的線程會阻塞,獲得鎖之後線程會被喚醒,阻塞和喚醒的操作是比較耗時間的,如果同步塊的代碼執行比較快,等待鎖的線程可以進行先進行自旋操作(就是不釋放CPU,執行一些空指令或者是幾次for循環),等待獲取鎖,這樣效率比較高。所以輕量級鎖天然瞄準不存在鎖競爭的場景,如果存在鎖競爭但不激烈,仍然可以用自旋鎖優化,自旋失敗后再升級為重量級鎖。

輕量級鎖的加鎖過程

JVM會為每個線程在當前線程的棧幀中創建用於存儲鎖記錄的空間,我們稱為Displaced Mark Word。如果一個線程獲得鎖的時候發現是輕量級鎖,會把鎖的Mark Word複製到自己的Displaced Mark Word裏面。

然後線程嘗試用CAS操作將鎖的Mark Word替換為自己線程棧中拷貝的鎖記錄的指針。如果成功,當前線程獲得鎖,如果失敗,表示Mark Word已經被替換成了其他線程的鎖記錄,說明在與其它線程競爭鎖,當前線程就嘗試使用自旋來獲取鎖。

自旋:不斷嘗試去獲取鎖,一般用循環來實現。

自旋是需要消耗CPU的,如果一直獲取不到鎖的話,那該線程就一直處在自旋狀態,白白浪費CPU資源。

JDK採用了適應性自旋,簡單來說就是線程如果自旋成功了,則下次自旋的次數會更多,如果自旋失敗了,則自旋的次數就會減少。

自旋也不是一直進行下去的,如果自旋到一定程度(和JVM、操作系統相關),依然沒有獲取到鎖,稱為自旋失敗,那麼這個線程會阻塞。同時這個鎖就會升級成重量級鎖。

輕量級鎖的釋放流程

在釋放鎖時,當前線程會使用CAS操作將Displaced Mark Word的內容複製回鎖的Mark Word裏面。如果沒有發生競爭,那麼這個複製的操作會成功。如果有其他線程因為自旋多次導致輕量級鎖升級成了重量級鎖,那麼CAS操作會失敗,此時會釋放鎖並喚醒被阻塞的線程。
輕量級鎖的加鎖解鎖流程圖:

重量級鎖

當多個線程同時請求某個重量級鎖時,重量級鎖會設置幾種狀態用來區分請求的線程:

Contention List:所有請求鎖的線程將被首先放置到該競爭隊列,我也不知道為什麼網上的文章都叫它隊列,其實這個隊列是先進后出的,更像是棧,就是當Entry List為空時,Owner線程會直接從Contention List的隊列尾部(后加入的線程中)取一個線程,讓它成為OnDeck線程去競爭鎖。(主要是剛來獲取重量級鎖的線程是回進行自旋操作來獲取鎖,獲取不到才會進從Contention List,所以OnDeck線程主要與剛進來還在自旋,還沒有進入到Contention List的線程競爭)

Entry List:Contention List中那些有資格成為候選人的線程被移到Entry List,主要是為了減少對Contention List的併發訪問,因為既會添加新線程到隊尾,也會從隊尾取線程。

Wait Set:那些調用wait方法被阻塞的線程被放置到Wait Set。

OnDeck:任何時刻最多只能有一個線程正在競爭鎖,該線程稱為OnDeck。

Owner:獲得鎖的線程稱為Owner

!Owner:釋放鎖的線程

重量級鎖執行流程:

流程圖如下:

步驟1是線程在進入Contention List時阻塞等待之前,程會先嘗試自旋使用CAS操作獲取鎖,如果獲取不到就進入Contention List隊列的尾部。

步驟2是Owner線程在解鎖時,如果Entry List為空,那麼會先將Contention List中隊列尾部的部分線程移動到Entry List

步驟3是Owner線程在解鎖時,如果Entry List不為空,從Entry List中取一個線程,讓它成為OnDeck線程,Owner線程並不直接把鎖傳遞給OnDeck線程,而是把鎖競爭的權利交給OnDeck,OnDeck需要重新競爭鎖,JVM中這種選擇行為稱為 “競爭切換”。(主要是與還沒有進入到Contention
List,還在自旋獲取重量級鎖的線程競爭)

步驟4就是OnDeck線程獲取到鎖,成為Owner線程進行執行。

步驟5就是Owner線程調用鎖對象的wait()方法進行等待,會移動到Wait Set中,並且會釋放CPU資源,也同時釋放鎖,

步驟6.就是當其他線程調用鎖對象的notify()方法,之前調用wait方法等待的這個線程才會從Wait Set移動到Entry List,等待獲取鎖。

3.為什麼說是輕量級,重量級鎖是不公平的?

偏向鎖由於不涉及到多個線程競爭,所以談不上公平不公平,輕量級鎖獲取鎖的方式是多個線程進行自旋操作,然後使用用CAS操作將鎖的Mark Word替換為指向自己線程棧中拷貝的鎖記錄的指針,所以誰能獲得鎖就看運氣,不看先後順序。重量級鎖不公平主要在於剛進入到重量級的鎖的線程不會直接進入Contention List隊列,而是自旋去獲取鎖,所以後進來的線程也有一定的幾率先獲得到鎖,所以是不公平的。

4.重量級鎖為什麼需要自旋操作?

因為那些處於ContetionList、EntryList、WaitSet中的線程均處於阻塞狀態,阻塞操作由操作系統完成(在Linxu下通過pthread_mutex_lock函數)。線程被阻塞后便進入內核(Linux)調度狀態,這個會導致系統在用戶態與內核態之間來回切換,嚴重影響鎖的性能。如果同步塊中代碼比較少,執行比較快的話,後進來的線程先自旋獲取鎖,先執行,而不進入阻塞狀態,減少額外的開銷,可以提高系統吞吐量。

5.什麼時候會發生鎖升級,鎖降級?

偏向鎖升級為輕量級鎖:
就是有不同的線程競爭鎖時。具體來看就是當一個線程發現當前鎖狀態是偏向鎖,然後鎖對象存儲的Thread id是其他線程的id,並且去Thread id對應的線程棧查詢到的lock record的obj字段不為null(代表當前持有偏向鎖的線程還在執行同步塊)。那麼該偏向鎖就會升級成輕量級鎖。

輕量級鎖升級為重量級鎖:
就是在輕量級鎖中,沒有獲取到鎖的線程進行自旋,自旋到一定次數還沒有獲取到鎖就會進行鎖升級,因為自旋也是佔用CPU的,長時間自旋也是很耗性能的。
鎖降級
因為如果沒有多線程競爭,還是使用重量級鎖會造成額外的開銷,所以當JVM進入SafePoint安全點(可以簡單的認為安全點就是所有用戶線程都停止的,只有JVM垃圾回收線程可以執行)的時候,會檢查是否有閑置的Monitor,然後試圖進行降級。

6.偏向鎖,輕量鎖,重量鎖的適用場景,優缺點是什麼?

篇幅有限,下面是各種鎖的優缺點,來自《併發編程的藝術》:

優點 缺點 適用場景
偏向鎖 加鎖和解鎖不需要額外的消耗,和執行非同步方法比僅存在納秒級的差距。 如果線程間存在鎖競爭,會帶來額外的鎖撤銷的消耗。 適用於只有一個線程訪問同步塊場景。
輕量級鎖 競爭的線程不會阻塞,提高了程序的響應速度。 如果始終得不到鎖競爭的線程使用自旋會消耗CPU。 追求響應時間。同步塊執行速度非常快。
重量級鎖 線程競爭不使用自旋,不會消耗CPU。 線程阻塞,響應時間緩慢。 追求吞吐量。同步塊執行速度較長。

參考鏈接:
https://github.com/farmerjohngit/myblog/issues/12
http://redspider.group:4000/article/02/9.html
https://blog.csdn.net/bohu83/article/details/51141836
https://blog.csdn.net/Dev_Hugh/article/details/106577862

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

【其他文章推薦】

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

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

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

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

【華為雲技術分享】數據庫開發:MySQL Seconds_Behind_Master簡要分析

【摘要】對於mysql主備實例,seconds_behind_master是衡量master與slave之間延時的一個重要參數。通過在slave上執行”show slave status;”可以獲取seconds_behind_master的值。

Seconds_Behind_Master

對於mysql主備實例,seconds_behind_master是衡量master與slave之間延時的一個重要參數。通過在slave上執行”show slave status;”可以獲取seconds_behind_master的值。

原始實現

Definition:The number of seconds that the slave SQL thread is behind processing the master binary log.

Type:time_t(long)

計算方式如下:

rpl_slave.cc::show_slave_status_send_data()
if ((mi->get_master_log_pos() == mi->rli->get_group_master_log_pos()) &&
       (!strcmp(mi->get_master_log_name(),
                mi->rli->get_group_master_log_name()))) {
     if (mi->slave_running == MYSQL_SLAVE_RUN_CONNECT)
       protocol->store(0LL);
     else
       protocol->store_null();
   } else {
     long time_diff = ((long)(time(0) - mi->rli->last_master_timestamp) -
                       mi->clock_diff_with_master);
     protocol->store(
         (longlong)(mi->rli->last_master_timestamp ? max(0L, time_diff) : 0));
   }

主要分為以下兩種情況:

• SQL線程等待IO線程獲取主機binlog,此時seconds_behind_master為0,表示備機與主機之間無延時;

• SQL線程處理relay log,此時seconds_behind_master通過(long)(time(0) – mi->rli->last_master_timestamp) – mi->clock_diff_with_master計算得到;

last_master_timestamp

定義:

• 主庫binlog中事件的時間。

• type: time_t (long)

計算方式:

last_master_timestamp根據備機是否并行複製有不同的計算方式。

非并行複製:

rpl_slave.cc:exec_relay_log_event()
if ((!rli->is_parallel_exec() || rli->last_master_timestamp == 0) &&
    !(ev->is_artificial_event() || ev->is_relay_log_event() ||
     (ev->common_header->when.tv_sec == 0) ||
     ev->get_type_code() == binary_log::FORMAT_DESCRIPTION_EVENT ||
     ev->server_id == 0))
{
 rli->last_master_timestamp= ev->common_header->when.tv_sec +
                             (time_t) ev->exec_time;
 DBUG_ASSERT(rli->last_master_timestamp >= 0);
}

在該模式下,last_master_timestamp表示為每一個event的結束時間,其中when.tv_sec表示event的開始時間,exec_time表示事務的執行時間。該值的計算在apply_event之前,所以event還未執行時,last_master_timestamp已經被更新。由於exec_time僅在Query_log_event中存在,所以last_master_timestamp在應用一個事務的不同event階段變化。以一個包含兩條insert語句的事務為例,在該代碼段的調用時,打印出event的類型、時間戳和執行時間

create table t1(a int PRIMARY KEY AUTO_INCREMENT ,b longblob) engine=innodb;
begin;
insert into t1(b) select repeat('a',104857600);
insert into t1(b) select repeat('a',104857600);
commit;
10T06:41:32.628554Z 11 [Note] [MY-000000] [Repl] event_type: 33 GTID_LOG_EVENT
2020-02-10T06:41:32.628601Z 11 [Note] [MY-000000] [Repl] event_time: 1581316890
2020-02-10T06:41:32.628614Z 11 [Note] [MY-000000] [Repl] event_exec_time: 0
2020-02-10T06:41:32.628692Z 11 [Note] [MY-000000] [Repl] event_type: 2   QUERY_EVENT
2020-02-10T06:41:32.628704Z 11 [Note] [MY-000000] [Repl] event_time: 1581316823
2020-02-10T06:41:32.628713Z 11 [Note] [MY-000000] [Repl] event_exec_time: 35
2020-02-10T06:41:32.629037Z 11 [Note] [MY-000000] [Repl] event_type: 19   TABLE_MAP_EVENT
2020-02-10T06:41:32.629057Z 11 [Note] [MY-000000] [Repl] event_time: 1581316823
2020-02-10T06:41:32.629063Z 11 [Note] [MY-000000] [Repl] event_exec_time: 0
2020-02-10T06:41:33.644111Z 11 [Note] [MY-000000] [Repl] event_type: 30    WRITE_ROWS_EVENT
2020-02-10T06:41:33.644149Z 11 [Note] [MY-000000] [Repl] event_time: 1581316823
2020-02-10T06:41:33.644156Z 11 [Note] [MY-000000] [Repl] event_exec_time: 0
2020-02-10T06:41:43.520272Z 0 [Note] [MY-011953] [InnoDB] Page cleaner took 9185ms to flush 3 and evict 0 pages
2020-02-10T06:42:05.982458Z 11 [Note] [MY-000000] [Repl] event_type: 19   TABLE_MAP_EVENT
2020-02-10T06:42:05.982488Z 11 [Note] [MY-000000] [Repl] event_time: 1581316858
2020-02-10T06:42:05.982495Z 11 [Note] [MY-000000] [Repl] event_exec_time: 0
2020-02-10T06:42:06.569345Z 11 [Note] [MY-000000] [Repl] event_type: 30    WRITE_ROWS_EVENT
2020-02-10T06:42:06.569376Z 11 [Note] [MY-000000] [Repl] event_time: 1581316858
2020-02-10T06:42:06.569384Z 11 [Note] [MY-000000] [Repl] event_exec_time: 0
2020-02-10T06:42:16.506176Z 0 [Note] [MY-011953] [InnoDB] Page cleaner took 9352ms to flush 8 and evict 0 pages
2020-02-10T06:42:37.202507Z 11 [Note] [MY-000000] [Repl] event_type: 16    XID_EVENT
2020-02-10T06:42:37.202539Z 11 [Note] [MY-000000] [Repl] event_time: 1581316890
2020-02-10T06:42:37.202546Z 11 [Note] [MY-000000] [Repl] event_exec_time: 0

并行複製:

rpl_slave.cc   mts_checkpoint_routine
ts = rli->gaq->empty()
          ? 0
          : reinterpret_cast<Slave_job_group *>(rli->gaq->head_queue())->ts;
 rli->reset_notified_checkpoint(cnt, ts, true);
 /* end-of "Coordinator::"commit_positions" */

在該模式下備機上存在一個分發隊列gaq,如果gaq為空,則設置last_commit_timestamp為0;如果gaq不為空,則此時維護一個checkpoint點lwm,lwm之前的事務全部在備機上執行完成,此時last_commit_timestamp被更新為lwm所在事務執行完成后的時間。該時間類型為time_t類型。

ptr_group->ts = common_header->when.tv_sec +
                   (time_t)exec_time;  // Seconds_behind_master related
rli->rli_checkpoint_seqno++;
if (update_timestamp) {
 mysql_mutex_lock(&data_lock);
 last_master_timestamp = new_ts;
 mysql_mutex_unlock(&data_lock);
}

在并行複製下,event執行完成之後才會更新last_master_timestamp,所以非并行複製和并行複製下的seconds_behind_master會存在差異。

clock_diff_with_master

定義:

• The difference in seconds between the clock of the master and the clock of the slave (second – first). It must be signed as it may be <0 or >0. clock_diff_with_master is computed when the I/O thread starts; for this the I/O thread does a SELECT UNIX_TIMESTAMP() on the master.

• type: long

rpl_slave.cc::get_master_version_and_clock()
if (!mysql_real_query(mysql, STRING_WITH_LEN("SELECT UNIX_TIMESTAMP()")) &&
     (master_res= mysql_store_result(mysql)) &&
     (master_row= mysql_fetch_row(master_res)))
 {
   mysql_mutex_lock(&mi->data_lock);
   mi->clock_diff_with_master=
     (long) (time((time_t*) 0) - strtoul(master_row[0], 0, 10));
   DBUG_EXECUTE_IF("dbug.mts.force_clock_diff_eq_0",
     mi->clock_diff_with_master= 0;);
   mysql_mutex_unlock(&mi->data_lock);
 }

該差值僅被計算一次,在master與slave建立聯繫時處理。

其他

exec_time

定義:

• the difference from the statement’s original start timestamp and the time at which it completed executing.

• type: unsigned long

struct timeval end_time;
ulonglong micro_end_time = my_micro_time();
my_micro_time_to_timeval(micro_end_time, &end_time);
exec_time = end_time.tv_sec - thd_arg->query_start_in_secs();

時間函數

(1)time_t time(time_t timer) time_t為long類型,返回的數值僅精確到秒;

(2)int gettimeofday (struct timeval *tv, struct timezone *tz) 可以獲得微秒級的當前時間;

(3)timeval結構

#include <time.h>
stuct timeval {
   time_t tv_sec; /*seconds*/
   suseconds_t tv_usec; /*microseconds*/
}

總結

使用seconds_behind_master衡量主備延時只能精確到秒級別,且在某些場景下,seconds_behind_master並不能準確反映主備之間的延時。主備異常時,可以結合seconds_behind_master源碼進行具體分析。

 

點擊關注,第一時間了解華為雲新鮮技術~

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

【其他文章推薦】

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

※別再煩惱如何寫文案,掌握八大原則!

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

※超省錢租車方案

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

※產品缺大量曝光嗎?你需要的是一流包裝設計!

DRY原則的一個簡單實踐

轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。

原文出處:https://dzone.com/articles/dry-dont-repeat-yourself

 

我們之前就發過一篇相關的文章:https://www.cnblogs.com/powertoolsteam/p/12758496.html 其中也提到了包括DRY在內的一些軟件開發的原則。

DRY 是軟件開發的原則之一,其目的主要是為了避免代碼重複,指導開發者盡量以抽象的思維去解決重複,基本上是,當您發現自己一遍又一遍地編寫相同的代碼時,可能會有更好的方法。 

實際案例

讓我們先看一個例子,看看這個例子是否可以改進,以及如何通過重構來避免代碼重複。

這裡有一個簡單的Report類,該類接收一些數據並通過控制台以格式化的方式直接輸出。

我們這裏使用php的一個代碼片段來舉例,相信大家對代碼的結構和想要完成的工作都不難理解,所以為了大家更容易理解,我只對一些下面用到的php函數定義做一個解釋:

  1. echo()  函數輸出一個或多個字符串
  2. ucwords()函數把字符串中每個單詞的首字符轉換為大寫。
  3. strtolower() 函數把字符串轉換為小寫。
  4. file_put_contents() 函數把一個字符串寫入文件中。
  5. floor() 函數向下舍入為最接近的整數。
class Report
{
   public function show(array $data)
   {
       echo "Report: " . ucwords(strtolower($data["name"])) . "\n";
       echo "Product: " . ucwords(strtolower($data["product"])) . "\n";
       echo "Start date: " . date("Y/m/d", $data["startDate"]) . "\n";
       echo "End date: " . date("Y/m/d", $data["endDate"]) . "\n";
       echo "Total: " . $data["total"] . "\n";
       echo "Average x day: " . floor($data["total"] / 365) . "\n";
       echo "Average x week: " . floor($data["total"] / 52) . "\n";
   }
}

可以看到,上面的代碼完成目標是沒有任何問題的。

這時我們對Report類提出一個新的需求:把所有字符串也可以保存到文件中。

我們經過一通複製和粘貼上面的代碼,新建一個名為saveToFile的函數,就可以很快的完成這個需求,代碼如下:

class Report
{
   public function show(array $data)
   {
       echo "Report: " . ucwords(strtolower($data["name"])) . "\n";
       echo "Product: " . ucwords(strtolower($data["product"])) . "\n";
       echo "Start date: " . date("Y/m/d", $data["startDate"]) . "\n";
       echo "End date: " . date("Y/m/d", $data["endDate"]) . "\n";
       echo "Total: " . $data["total"] . "\n";
       echo "Average x day: " . floor($data["total"] / 365) . "\n";
       echo "Average x week: " . floor($data["total"] / 52) . "\n";
       echo "Average x month: " . floor($data["total"] / 12) . "\n";
   }
   public function saveToFile(array $data)
   {
       $report = '';
       $report .= "Report: " . ucwords(strtolower($data["name"])) . "\n";
       $report .= "Product: " . ucwords(strtolower($data["product"])) . "\n";
       $report .= "Start date: " . date("Y/m/d", $data["startDate"]) . "\n";
       $report .= "End date: " . date("Y/m/d", $data["endDate"]) . "\n";
       $report .= "Total: " . $data["total"] . "\n";
       $report .= "Average x day: " . floor($data["total"] / 365) . "\n";
       $report .= "Average x week: " . floor($data["total"] / 52) . "\n";
       $report .= "Average x month: " . floor($data["total"] / 12) . "\n";
       file_put_contents("./report.txt", $report);
   }
}

那麼,上面的代碼能夠滿足我們提出的需求嗎?答案當然“是的”。但是從技術角度來看,這段代碼似乎是有些問題的,它的重複代碼到處都是。無論是對代碼閱讀及後期維護來講,這都是一場噩夢。

所以我們需要進行一些重構,抽象能抽象的方法,讓冗繁的代碼變得更簡潔。

首先,我們對Report類進行功能上的抽象,生成報告並輸出一共可以分為兩個功能,一個只負責創建Report,一個只負責如何處理Report,那麼讓我們開始重構吧。  

class Report
{
   public function show(array $data)
   {
       echo $this->createReport($data);
   }
   public function saveToFile(array $data)
   {
       file_put_contents("./report.txt", $this->createReport($data));
   }
   private function createReport(array $data): string
   {
       $report = '';
       $report .= "Report: " . ucwords(strtolower($data["name"])) . "\n";
       $report .= "Product: " . ucwords(strtolower($data["product"])) . "\n";
       $report .= "Start date: " . date("Y/m/d", $data["startDate"]) . "\n";
       $report .= "End date: " . date("Y/m/d", $data["endDate"]) . "\n";
       $report .= "Total: " . $data["total"] . "\n";
       $report .= "Average x day: " . floor($data["total"] / 365) . "\n";
       $report .= "Average x week: " . floor($data["total"] / 52) . "\n";
       $report .= "Average x month: " . floor($data["total"] / 12) . "\n";
       return $report;
   }
}

現在看起來更清楚一些,對嗎?

下面我們還有函數使用重複的問題要解決,例如,Report和Products的名稱函數使用重複:

$report .= "Report: " . ucwords(strtolower($data["name"])) . "\n";
$report .= "Product: " . ucwords(strtolower($data["product"])) . "\n";

我們可以將這些轉換抽象為一個新的函數:

private function normalizeName($name): string
{
   return ucwords(strtolower($name));
}

另一個重複:日期格式。

$report .= "Start date: " . date("Y/m/d", $data["startDate"]) . "\n";
$report .= "End date: " . date("Y/m/d", $data["endDate"]) . "\n";

讓我們將其抽象為:

private function formatDate($date): string
{
   return date("Y/m/d", $date);
}

最後一個:平均值計算。

$report .= "Average x day: " . floor($data["total"] / 365) . "\n";
$report .= "Average x week: " . floor($data["total"] / 52) . "\n";
$report .= "Average x month: " . floor($data["total"] / 12) . "\n";

儘管計算結果並不完全相同,但執行的操作大家是一致的,所以可以抽象為如下:

private function calculateAverage(array $data, $period): string
{
   return floor($data["total"] / $period);
}

所以,經過了一番重構,最終的Report類變為了如下:

class Report
{
   public function show(array $data)
   {
       echo $this->createReport($data);
   }
   public function saveToFile(array $data)
   {
       file_put_contents("./report.txt", $this->createReport($data));
   }
   private function createReport(array $data)
   {
       $report = '';
       $report .= "Report: " . $this->normalizeName($data["name"]) . "\n";
       $report .= "Product: " . $this->normalizeName($data["product"]) . "\n";
       $report .= "Start date: " . $this->formatDate($data["startDate"]) . "\n";
       $report .= "End date: " . $this->formatDate($data["endDate"]) . "\n";
       $report .= "Total: " . $data["total"] . "\n";
      $report .= "Average x day: " . $this->calculateAverage($data, 365) . "\n";
      $report .= "Average x week: " . $this->calculateAverage($data, 52) . "\n";
      $report .= "Average x month: " . $this->calculateAverage($data, 12) . "\n";
      return $report;
  }
  private function formatDate($date): string
  {
       return date("Y/m/d", $date);
   }
  private function calculateAverage(array $data, $period): string
  {
      return floor($data["total"] / $period);
  }
  private function normalizeName($name): string
  {
      return ucwords(strtolower($name));
  }
}

這是一個簡單的例子,實際情況可能比這要更加複雜的多,但我僅想通過這個實例向大家說明一個問題,那就是避免重複代碼的重要性及我們如何通過重構去處理重複代碼。

有時候重複一次相同的代碼可能沒問題,但是當第三次​​我們寫出相同的代碼時,那就說明是時候重構你的代碼了。

結論:

請記住DRY原則,並隨時抱着不要重複自己代碼的想法去完成開發工作。

 

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

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

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

※超省錢租車方案

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

網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!