JS中的相等性判斷===, ==, Object.is()

相信剛接觸JS的人都會被他的想等性判斷給整糊塗,看看下面代碼,你能答對幾個?

NaN === NaN // false
NaN == NaN // false
Object.is(NaN, NaN) // true
0 == false  // true
1 == true   // true
Number(null) === 0  // true
null == 0   // false

Javascript提供了三種不同的值比較操作,分別是嚴格相等、寬鬆相等、以及Object.is,今天查完資料后做了一下總結,希望下面的內容能夠對大家有所幫助,如果哪些地方有誤的話,也歡迎大家指正。

1. 嚴格相等 x === y判斷邏輯

  1. 如果x的數據類型和y的數據類型不相同,返回false;
  2. 如果x是Number類型
    • x是NaN,返回false
    • y是NaN,返回false
    • x的值和y的值相等,返回true
    • x是+0,y是-0,返回true
    • x是-0,y是+0,返回true
    • 否則返回false
  3. 其他類型參照SameValueNonNumber(x, y)
    • 斷言:x,y不是Number類型;
    • 斷言: x,y的數據類型相同;
    • x是undefined, y是undefined return true;
    • x是null, y是null,return true;
    • x是字符串類型,當且僅當x,y字符序列完全相同時(長度相同,每個位置上的字符也相同)返回true, 否則返回false;
    • 如果x是布爾類型,當x,y都為true或者都為false時返回true,否則返回false;
    • 如果x是symbol類型,當x,y是相同的symbol值,返回true,否則返回false;
    • 如果x,y是同一個對象值,返回true,否則返回false;
NaN === NaN // false
undefined === undefined // true
null === null   // true
undefined === null  // false

2. 寬鬆相等 x == y

  1. 如果x,y的類型相同,返回x===y的結果;
  2. 如果x是null, y是undefined, 返回true;
  3. 如果x是undefined, y是null, 返回true;
  4. 如果x是數值,y是字符串, 返回x == ToNumber(y);
  5. 如果x是字符串,y是數值, 返回ToNumber(x) == y;
  6. 如果x是布爾類型, 返回ToNumber(x)==y 的結果;
  7. 如果y是布爾類型,返回 x==ToNumber(y) 的結果;
  8. 如果x是String或Number或Symbol中的一種並且Type(y)是Object,返回 x==ToPrimitive(y) 的結果
  9. 如果Type(x)是Object並且Type(y)是String或Number或Symbol中的一種,返回 ToPrimitive(x)==y 的結果
  10. 其他返回false
12 == '0xc' // true, 0xc是16進制
12 == '12'  // true
12 == '12c' // false, 說明ToNumber轉換是用的Number()方法

注意

Number(null) === 0
但是
null == 0 // false, 

2.1 ToNumber將一個值轉換為數值類型

詳情參考

  1. 如果是boolean類型, true返回1,false返回0;
  2. 如果是數值,只是簡單的傳入返回;
  3. 如果是null,返回0
  4. 如果是undefined, 返回NaN;
  5. 如果是字符串,字符串如果只包含数字,則將其轉換成十進制數;如果是有效的浮點格式,將其轉換成對應的浮點數值;如果是二進制或十六進制將其轉換成對應的十進制數值;
  6. 如果是對象,調用對象的valueOf()方法,然後依照前面規則轉換,如果valueOf返回值是NaN,則調用toString()方法,再依照前面的規則轉換返回的字符串

2.2 ToPrimitive

toPrimitive(A)通過嘗試調用 A 的A.toString() 和 A.valueOf() 方法,將參數 A 轉換為原始值(Primitive);
JS中原始類型有:Number、String、Boolean、Null、Undefined;

不同類型對象的valueOf()方法的返回值:

對象 返回值
Array 返回數組對象本身。
Boolean 布爾值
Date 存儲的時間是從 1970 年 1 月 1 日午夜開始計的毫秒數 UTC
Function 函數本身
Number 数字值
Object 對象本身。這是默認情況, 可以覆蓋自定義對象的valueOf方法
String 字符串值
// Array:返回數組對象本身
var array = ["ABC", true, 12, -5];
console.log(array.valueOf() === array);   // true

// Date:當前時間距1970年1月1日午夜的毫秒數
var date = new Date(2013, 7, 18, 23, 11, 59, 230);
console.log(date.valueOf());   // 1376838719230

// Number:返回数字值
var num =  15.26540;
console.log(num.valueOf());   // 15.2654

// 布爾:返回布爾值true或false
var bool = true;
console.log(bool.valueOf() === bool);   // true

// new一個Boolean對象
var newBool = new Boolean(true);
// valueOf()返回的是true,兩者的值相等
console.log(newBool.valueOf() == newBool);   // true
// 但是不全等,兩者類型不相等,前者是boolean類型,後者是object類型
console.log(newBool.valueOf() === newBool);   // false

// Function:返回函數本身
function foo(){}
console.log( foo.valueOf() === foo );   // true
var foo2 =  new Function("x", "y", "return x + y;");
console.log( foo2.valueOf() );
/*
ƒ anonymous(x,y
) {
return x + y;
}
*/

// Object:返回對象本身
var obj = {name: "張三", age: 18};
console.log( obj.valueOf() === obj );   // true

// String:返回字符串值
var str = "http://www.xyz.com";
console.log( str.valueOf() === str );   // true

// new一個字符串對象
var str2 = new String("http://www.xyz.com");
// 兩者的值相等,但不全等,因為類型不同,前者為string類型,後者為object類型
console.log( str2.valueOf() === str2 );   // false

3.同值相等

同值相等由 Object.is 方法判斷:

  • 兩個值都是 undefined
  • 兩個值都是 null
  • 兩個值都是 true 或者都是 false
  • 兩個值是由相同個數的字符按照相同的順序組成的字符串
  • 兩個值指向同一個對象
  • 兩個值都是数字並且
    • 都是正零 +0,
    • 或者都是負零 -0,
    • 或者都是 NaN
    • 都是除零和 NaN 外的其它同一個数字
Object.is('foo', 'foo');     // true
Object.is(window, window);   // true

Object.is('foo', 'bar');     // false
Object.is([], []);           // false

var foo = { a: 1 };
var bar = { a: 1 };
Object.is(foo, foo);         // true
Object.is(foo, bar);         // false

Object.is(null, null);       // true

Object.is(true, 'true')     // false

// 特例
Object.is(0, -0);            // false
Object.is(0, +0);            // true
Object.is(-0, -0);           // true
Object.is(NaN, 0/0);         // true

4.零值相等

與同值相等類似,不過會認為 +0 與 -0 相等。

小結

  • === 不做類型轉換,當兩邊的數類型不相同時,直接返回false;當前類型相同且都是數值類型的時候,有一個是NaN,那麼結果就是false, 另外 +0 === -0
  • ==運算符,當兩邊操作數類不相同時會做隱式轉換,然後才進行比較,這樣的話就會出現 false == 0, ” == false 等現象, 但是Object.is不會做這種轉換

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

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

※帶您來看台北網站建置台北網頁設計,各種案例分享

核災後九年 福島縣計畫轉型為再生能源樞紐

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

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

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

※帶您來看台北網站建置台北網頁設計,各種案例分享

借中國優勢,奧迪大力發展電動車

面對“2020年二氧化碳排放95克/公里”的嚴苛法規,歐洲車企將在未來幾年密集發佈純電動車、插電式混合動力車。此環境下,奧迪也在積極部署電動車發展規劃。  
2016年奧迪A6 e-tron在中國生產   在電動車市場上,隨著中國政府政策激勵,各地建設充電樁、充電站將出現高潮,購買使用電動車的門檻降低,促進新能源車市場加快發展。   奧迪管理董事會主席施泰德在2015年法蘭克福車展上透露,奧迪A6 e-tron以及其使用的鋰離子電池將於2016年啟動中國生產。據悉,這款新能源車由中德工程團隊聯合研發,並針對中國的市場需求、使用工況進行改進。  
2020年後,奧迪新能源車占市場15%-20%份額   施泰德判斷,2020年後,純電動車、插電混合動力車會出現發展的高峰期,奧迪旗下的新能源產品有望實現15%-20%的市場份額。   在中國的消費結構上,豪華車市場仍在正面、快速地發展,豪華車的消費占比會突破12%,甚至有望達到15%。動力電池技術日趨成熟,e-tron quattro已經實現500公里的續航里程,中國產奧迪A6 e-tron導入最新的動力電池技術、能量管理方案以提升產品的競爭力。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

※帶您來看台北網站建置台北網頁設計,各種案例分享

鴻海與騰訊等合資公司「和諧富騰」 工廠於 2018 年量產

鴻海集團電動車布局邁入最後衝刺階段,市場傳出,鴻海旗下富士康與騰訊、和諧汽車在大陸鄭州合資的電動車計畫,工廠將在 2018 年開始大量量產,並將結合鴻準、群創、 F-乙盛等「鴻家軍」成員,搶食大陸市場電動車商機。   鴻海、騰訊與和諧是在今年 6 月共同宣布在鄭州成立合資公司「和諧富騰」,搶進電動車市場。市場人士認為,這三方合作,各取自家集團單字做為公司名稱的「和諧富騰」,預料將結合現今物聯網新趨勢,打造與眾不同的智慧電動小客車。「和諧富騰」初期資本額人民幣 10 億元(約新台幣 50 億元),由和諧持股 40%、鴻海 30%、深圳騰訊 30%。   最新消息傳出,「和諧富騰」的工廠有望明、後年完工,2018 年量產,主打中高階市場,有望成為「電動車界的蘋果」,以精品概念搶市。市場認為,這三方合作的「鑽石組合」,將由鴻海負責拿手的電子相關設備生產,騰訊運用其在互聯網產業的平台,配合和諧在金字塔頂端、豪華汽車經銷商的服務利基,具有非常大的競爭優勢。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

※帶您來看台北網站建置台北網頁設計,各種案例分享

SqlServer設置特定用戶操作特定表(插入、刪除、更新、查詢 的權限設置)

目錄

一、需求場景:

在和其他廠商對接的時候,經常會將某數據庫的某些表放開增刪改查(CRUD)的權限給對方,此時,就要新建個用戶,授予某數據庫的某些表CRUD的權限;還有的時候只需授權SELECT權限,其他權限不能授予;

二、操作步驟:

演示所用數據庫版本:SQL Server Management Studio V17.8.1

1、登錄SSMS后選擇【安全性】,右鍵點擊【登錄名】,在彈出的右鍵菜單中選擇【新建登錄名】選項;

2、在【常規】選項卡中,創建登錄名,並設置默認數據庫;

3、在【用戶映射】選顯卡中,勾選數據庫,並設置【架構】;

4、對需要設置操作權限的表進行權限設置,【表】–【屬性】;

數據庫TestDB中已經有兩個表,分別為User和Salary,現在對錶User進行權限設置,將Insert、update、delete、select的權限賦給用戶【zyl】;
1)、表上右鍵選擇【屬性】,選擇【權限】選項卡:

2)、點擊【搜索】,在彈出的框中點擊【瀏覽】,選擇需要設置的用戶;

3)、在上面點擊【確定】后,就可以在【權限】選項卡中看到權限列表,選擇需要的權限點擊確定即可;

4)、當選中【選擇】和【引用】權限時,還可以設置【列權限】;

5、使用【zyl】登錄數據庫,就只能看到User表了;

並且用戶【zyl】只能對錶User進行增刪改查,不能修改表結構;

三、注意事項

1、在權限列表中如果選擇了【設置】這項,那麼再設置【列權限】就沒有意義了,因為查詢已經不受限制了;

如果設置了【列權限】,沒有選擇【設置】權限,那麼select * 就會報錯;

2、除了對錶設置權限外,還可以對視圖、函數等進行權限設置;

在數據庫的【安全性】-【用戶】-【zyl】-【屬性】-【安全對象】;

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

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

※帶您來看台北網站建置台北網頁設計,各種案例分享

馬紹爾深受暖化威脅 前總統籲勿視若無睹

摘錄自2020年1月16日中央社報導

台灣的太平洋友邦馬紹爾群島是受氣候變遷威脅最大的國家之一,前總統海妮受訪時不僅表示遺憾有些國家未嚴肅看待氣候議題,還提到執政時曾接受台灣援助促進女權。

路透社報導,在海拔低的馬紹爾群島,全球暖化造成海平面緩慢升高,連卸任總統海妮(Hilda Heine)都必須在她樸素的家裡處理漫入花園的鹹海水。她說,「情況一年比一年糟」。

馬紹爾群島由超過30個熱帶珊瑚環礁組成,其中多數環礁的平均海拔僅約兩公尺。海妮受訪時說:「全世界都在談氣候變遷,對我們而言,這是攸關存亡的威脅,我們已經盡力把這項議題搬上國際場域了。」

過去四年,馬紹爾群島發布八次衛生緊急狀態,包括透過蚊子傳染的登革熱、茲卡病毒、屈公病疫情爆發,海妮認為這都與氣候變遷有關。

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

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

※帶您來看台北網站建置台北網頁設計,各種案例分享

特斯拉:把電動汽車裡的電賣回給電網未來或可實現

近日,特斯拉中國公共策劃與充電基礎設施總監高翔在接受記者採訪時表示,“充電並不是一件很難的事情“,甚至以後可以“把電賣給電網“。

電動汽車充電並不難

目前,對比汽油車加油,電動車充電仍然給人們一種“充電還是很困難“的感覺。高翔對此解釋道,汽油車發展超過100年了,剛開始也是很困難,但是現在到處都有加油站,我們也覺得很方便。而事實上,充電並沒有我們想像中那麼難,舉例來說,電動車充電並不需要像加油站那樣大的土地,也不需要擔憂運輸、儲油的安全性問題。

據悉,目前特斯拉汽車的充電方式有三種:一是家庭充電,充電設備隨車贈送;二是建在各大商場、酒店、旅遊地的“目的地充電樁”,方便車主外出時充電。跟家電充電一樣,目的地充電樁同樣是支援220V電壓,所以充電時間較長,充滿一次需要11.5-13小時左右;三是超級充電站。由於其配備的超級充電器是通過特製電纜繞過車載充電設備,直接將380V的直流電輸入電池,所以充電時間大為縮短。目的地充電樁每充電1小時可行駛至少50公里,而使用超級充電站,20分鐘就能充滿一半,80分鐘即可完全充滿,並可行駛400公里以上。並且車主在此可以免費充電。

電動車發展明年將會進入爆發式增長

為什麼大家對發展電動汽車有很深的理解,一個是霧霾,近幾年日益嚴重;另外一個是能源危機,我們採用的化石能源60%左右依賴於進口。在能源危機和環境壓力面前,我們需要很好的解決方案。而在出行方式裡面,汽油車占到90%的汽油消耗量,所以電動汽車對這方面有很大的幫助。

同時,國家新的標準馬上也要發佈,高翔預測,整個電動車行業到2016年將會進入一個爆發式增長,甚至在以後,國家除了對車進行補貼外,也將會對基礎設施進行支持,大力推進基礎設施充電的發展。有了這些,剩下的就是企業把車做好。未來充電市場和電動車行業一定是相輔相成的。

把車裡的電賣給電網

電動汽車行業與電網本來就是天然的合作關係。電動汽車除了把電能作為能源之外,其實也是能源互聯網以及智慧電網中的一個環節。據高翔介紹,電網作為一個承擔著中國電力行業社會責任的企業,對於電動汽車的發展一直都是支援的,特別是北上廣深等大城市,在這方面有著非常清晰的政策。

而從長遠來看,電動汽車的發展對電網來說也是一個好事。充電設施多了,對電網企業了來說,最重要的絕不是“充個電、收個服務費“,而是未來發展智慧電網、甚至是發展車和電網的互聯互通。比如說VTOG(雙向逆變充放電技術),從車反過來給電網充電,充好電的車相當於無數個儲能電池,對電網起到削峰填穀的作用,也就是說,晚上用不掉的電儲存到車裡來,白天用電高峰時候電動汽車用戶再把這些電散落地賣還給電網。

高翔解釋道,現在電網發電還做不到即時的調控,要滿足高峰用電,就需要按照需求最大值來發電,但是電網大多數時間是用不了這麼多電的,晚上好多電用不掉但是也不能停,否則經濟損失很大。如果採取了上面這種做法的話,不但車主能得到收益,電網也能節省很大的發電及儲能成本,將是一個非常理想的削峰填穀模式。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

※帶您來看台北網站建置台北網頁設計,各種案例分享

JVM原理速記複習Java虛擬機總結思維導圖面試必備

良心製作,右鍵另存為保存

喜歡可以點個贊哦

Java虛擬機

一、運行時數據區域

線程私有

  • 程序計數器

    • 記錄正在執行的虛擬機字節碼指令的地址(如果正在執行的是Native方法則為空),是唯一一個沒有規定OOM(OutOfMemoryError)的區域。
  • Java虛擬機棧

    • 每個Java方法在執行的同時會創建一個棧楨用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。從方法調用直到執行完成的過程,對應着一個棧楨在Java虛擬機棧中入棧和出棧的過程。(局部變量包含基本數據類型、對象引用reference和returnAddress類型)
  • 本地方法棧

    • 本地方法棧與Java虛擬機棧類似,它們之間的區別只不過是本地方法棧為Native方法服務。

線程公有

  • Java堆(GC區)(Java Head)

    • 幾乎所有的對象實例都在這裏分配內存,是垃圾收集器管理的主要區域。分為新生代和老年代。對於新生代又分為Eden空間、From Survivor空間、To Survivor空間。
  • JDK1.7 方法區(永久代)

    • 用於存放已被加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據。
      對這塊區域進行垃圾回收的主要目的是對常量池的回收和對類的卸載,但是一般難以實現。
      HotSpot虛擬機把它當做永久代來進行垃圾回收。但很難確定永久代的大小,因為它受到很多因素的影響,並且每次Full GC之後永久代的大小都會改變,所以經常拋出OOM異常。
      從JDK1.8開始,移除永久代,並把方法區移至元空間。
    • 運行時常量池

      • 是方法區的一部分
        Class文件中的常量池(編譯器生成的字面量和符號引用)會在類加載后被放入這個區域。
        允許動態生成,例如String類的intern()
  • JDK1.8 元空間

    • 原本存在方法區(永久代)的數據,一部分移到了Java堆裏面,一部分移到了本地內存裏面(即元空間)。元空間存儲類的元信息,靜態變量和常量池等放入堆中。
  • 直接內存

    • 在NIO中,會使用Native函數庫直接分配堆外內存。

二、HotSpot虛擬機

對象的創建

  • 當虛擬機遇到一條new指令時
  1. 檢查參數能否在常量池中找到符號引用,並檢查這個符號引用代表的類是否已經被加載、解析和初始過,沒有的話先執行相應的類加載過程。
  2. 在類加載檢查通過之後,接下來虛擬機將為新生對象分配內存。
  3. 內存分配完成之後,虛擬機需要將分配到的內存空間都初始化為零值(不包括對象頭)。
  4. 對對象頭進行必要的設置。
  5. 執行構造方法按照程序員的意願進行初始化。

對象的內存布局

    1. 對象頭
      1. 第一部分用於存儲對象自身的運行時數據,如哈希碼、GC分代年齡、鎖狀態標識、線程持有的鎖、偏向線程ID、偏向實現戳等。
      1. 第二部分是類型指針,即對象指向它的類元數據的指針(如果使用直接對象指針訪問),虛擬機通過這個指針來確定這個對象是哪個類的實例。
      1. 如果對象是一個Java數組的話,還需要第三部分記錄數據長度的數據。
    1. 實例數據
    • 是對象真正存儲的有效信息,也就是在代碼中定義的各種類型的字段內容。
    1. 對齊填充
    • 不是必然存在的,僅僅起着佔位符的作用。
      HotSpot需要對象的大小必須是8字節的整數倍。

對象的訪問定位

  • 句柄訪問

    • 在Java堆中劃分出一塊內存作為句柄池。
      Java棧上的對象引用reference中存儲的就是對象的句柄地址,而句柄中包含了到對象實例數據的指針和到對象類型數據的指針。
      對象實例數據在Java堆中,對象類型數據在方法區(永久代)中。
      優點:在對象被移動時只會改變句柄中的實例數據指針,而對象引用本身不需要修改。
  • 直接指針訪問(HotSpot使用)

    • Java棧上的對象引用reference中存儲的就是對象的直接地址。
      在堆中的對象實例數據就需要包含到對象類型數據的指針。
      優點:節省了一次指針定位的時間開銷,速度更快。

三、垃圾收集

概述

  • 垃圾收集主要是針對Java堆和方法區。
    程序計數器、Java虛擬機棧個本地方法棧三個區域屬於線程私有,線程或方法結束之後就會消失,因此不需要對這三個區域進行垃圾回收。

判斷對象是否可以被回收

  • 第一次標記(緩刑)

    • 引用計數算法

      • 給對象添加一個引用計數器,當對象增加一個引用時引用計數值++,引用失效時引用計數值–,引用計數值為0時對象可以被回收。

但是它難以解決對象之間的相互循環引用的情況,此時這個兩個對象引用計數值為1,但是永遠無法用到這兩個對象。

- 可達性分析算法(Java使用)

    - 以一系列GC Roots的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為引用鏈,當一個對象到GC Roots沒有任何引用鏈相連是,則證明此對象不可用,可以被回收。

GC Roots對象包括

  1. 虛擬機棧(棧楨中的本地變量表)中引用的對象。
  2. 方法區中共類靜態屬性引用的對象。
  3. 方法區中常量引用的對象。
  4. 本地方法棧中JNI(即一般說的Native方法)引用的對象。
  • 第二次標記

    • 當對象沒有覆蓋finalize()方法,或者finalize()方法已經被虛擬機調用過。
      如果對象在finalize方法中重新與引用鏈上的任何一個對象建立關聯則將不會被回收。
    • finalize()

      • 任何一個對象的finalize()方法都只會被系統調用一次。
        它的出現是一個妥協,運行代價高昂,不確定性大,無法保證各個對象的調用順序。
        finalize()能做的所有工作使用try-finally或者其他方式都可以做的更好,完全可以忘記在這個函數的存在。

方法區的回收

  • 在方法區進行垃圾回收的性價比一般比較低。
    主要回收兩部分,廢棄常量和無用的類。

滿足無用的類三個判斷條件才僅僅代表可以進行回收,不是必然關係,可以使用-Xnoclassgc參數控制。

  1. 該類的所有實例都已經被回收,也就是Java堆中不存在該類的任何實例。
  2. 加載該類的ClassLoader已經被回收。
  3. 該類對應的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問到該類的方法。

引用類型

    1. 強引用
    • 使用new一個新對象的方式來創建強引用。
      只要強引用還存在,被引用的對象則永遠不會被回收。
    1. 軟引用
    • 使用SoftReference類來實現軟引用。
      用來描述一些還有用但是並非必須的對象,被引用的對象在將要發生內存溢出異常之前會被回收。
    1. 弱引用
    • 使用WeakReference類來實現弱引用。
      強度比軟引用更弱一些,被引用的對象在下一次垃圾收集時會被回收。
    1. 虛引用
    • 使用PhantomReference類來實現虛引用。
      最弱的引用關係,不會對被引用的對象生存時間構成影響,也無法通過虛引用來取得一個對象實例。
      唯一目的就是能在這個對象被收集器回收時收到一個系統通知。

垃圾收集算法

    1. 標記 – 清除
    • 首先標記出所有需要回收的對象,在標記完成后統一回收被標記的對象並取消標記。

不足:

  1. 效率問題,標記和清除兩個過程的效率都不高。
  2. 空間問題,標記清除之後會產生大量不連續的內存碎片,沒有連續內存容納較大對象而不得不提前觸發另一次垃圾收集。
    1. 標記 – 整理
    • 和標記 – 清除算法一樣,但標記之後讓所有存活對象都向一段移動,然後直接清理掉端邊界以外的內存。
      解決了標記 – 清除算法的空間問題,但需要移動大量對象,還是存在效率問題。
    1. 複製
    • 將可用內存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活着的對象複製到另外一塊上面,然後再把已使用多的內存空間一次清理掉。
      代價是將內存縮小為原來的一般,太高了。

現在商業虛擬機都採用這種算法用於新生代。
因為新生代中的對象98%都是朝生暮死,所以將內存分為一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和其中一塊Survivor空間。
當回收時,如果另外一塊Survivor空間沒有足夠的空間存放存活下來的對象時,這些對象將直接通過分配擔保機制進入老年代。

    1. 分代收集
    • 一般把Java堆分為新生代和老年代。
      在新生代中使用複製算法,在老年代中使用標記 -清除 或者 標記 – 整理 算法來進行回收。

HotSpot的算法實現

  • 枚舉根節點(GC Roots)

    • 目前主流Java虛擬機使用的都是準確式GC。
      GC停頓的時候,虛擬機可以通過OopMap數據結構(映射表)知道,在對象內的什麼偏移量上是什麼類型的數據,而且特定的位置記錄著棧和寄存器中哪些位置是引用。因此可以快速且準確的完成GC Roots枚舉。
  • 安全點

    • 為了節省GC的空間成本,並不會為每條指令都生成OopMap,只是在“特定的位置”記錄OopMap,這些位置稱為安全點。

程序執行只有到達安全點時才能暫停,到達安全點有兩種方案。

  1. 搶斷式中斷(幾乎不使用)。GC時,先把所有線程中斷,如果有線程不在安全點,就恢復該線程,讓他跑到安全點。
  2. 主動式中斷(主要使用)。GC時,設置一個標誌,各個線程執行到安全點時輪詢這個標誌,發現標誌為直則掛起線程。

但是當線程sleep或blocked時無法響應JVM的中斷請求走到安全點中斷掛起,所以引出安全區域。

  • 安全區域

    • 安全區域是指在一段代碼片段之中,引用關係不會發生變化,是擴展的安全點。

線程進入安全區域時表示自己進入了安全區域,這個發生GC時,JVM就不需要管這個線程。
線程離開安全區域時,檢查系統是否完成GC過程,沒有就等待可以離開安全區域的信號為止,否者繼續執行。

垃圾收集器

  • 新生代

      1. serial收集器
      • 它是單線程收集器,只會使用一個線程進行垃圾收集工作,更重要的是它在進行垃圾收集時,必須暫停其他所有的工作線程。

優點:對比其他單線程收集器簡單高效,對於單個CPU環境來說,沒有線程交互的開銷,因此擁有最高的單線程收集效率。

它是Client場景下默認新生代收集器,因為在該場景下內存一般來說不會很大。

- 2. parnew收集器

    - 它是Serial收集器的多線程版本,公用了相當多的代碼。

在單CPU環境中絕對不會有比Serial收集器更好的效果,甚至在2個CPU環境中也不能百分之百超越。

它是Server場景下默認的新生代收集器,主要因為除了Serial收集器,只用它能與CMS收集器配合使用。

- 3. parallel scavenge收集器

    - “吞吐優先”收集器,與ParNew收集器差不多。

但是其他收集器的目標是盡可能縮短垃圾收集時用戶線程停頓的時間,而它的目標是達到一個可控制的吞吐量。這裏的吞吐量指CPU用於運行用戶程序的時間佔總時間的比值。

  • 老年代

      1. serial old收集器
      • 是Serial收集器老年代版本。

也是給Client場景下的虛擬機使用的。

- 5. parallel old收集器

    - 是Parallel Scavenge收集器的老年代版本。

在注重吞吐量已經CPU資源敏感的場合,都可以優先考慮Parallel Scavenge和Parallel Old收集器。

- 6. cms收集器

    - Concurrent Mark Sweep收集器是一種以獲取最短回收停頓時間為目標的收集器。
    - 運作過程

        - 1. 初始標記(最短)。仍需要暫停用戶線程。只是標記一下GC Roots能直接關聯到的對象,速度很快
  1. 併發標記(耗時最長)。進行GC Roots Tracing(根搜索算法)的過程。
  2. 重新標記。修正併發標記期間因用戶程序繼續運作而導致標記產生變動的那一部分對象的標記記錄。比初始標記長但遠小於併發標記時間。
  3. 併發清除

1 和4 兩個步驟並沒有帶上併發兩個字,即這兩個步驟仍要暫停用戶線程。

    - 優缺點

        - 併發收集、低停頓。
  1. CMS收集器對CPU資源非常敏感。雖然不會導致用戶線程停頓,但是佔用CPU資源會使應用程序變慢。
  2. 無法處理浮動垃圾。在併發清除階段新垃圾還會不斷的產生,所以GC時要控制“-XX:CMSinitiatingOccupancyFraction參數”預留足夠的內存空間給這些垃圾,當預留內存無法滿足程序需要時就會出現”Concurrent Mode Failure“失敗,臨時啟動Serial Old收集。
  3. 由於使用標記 – 清除算法,收集之後會產生大量空間碎片。
    1. g1收集器
    • Garbage First是一款面向服務端應用的垃圾收集器
    • 運作過程

        1. 初始標記
  1. 併發標記
  2. 最終標記
  3. 刪選標記

五、類加載機制

概述

  • 虛擬機把描述類的數據從Class問價加載到內存,並對數據進行校驗、轉換解析和初始化,最終形成可以被虛擬機直接使用的Java類型。
    Java應用程序的高度靈活性就是依賴運行期動態加載和動態連接實現的。

類的生命周期

  • 加載 -> 連接(驗證 -> 準備 -> 解析) -> 初始化 -> 使用 – >卸載

類初始化時機

  • 主動引用

    • 虛擬機規範中沒有強制約束何時進行加載,但是規定了有且只有五種情況必須對類進行初始化(加載、驗證、準備都會隨之發生)
  1. 遇到new、getstatic、putstatic、invokestatic這四條字節碼指令時沒有初始化。
  2. 反射調用時沒有初始化。
  3. 發現其父類沒有初始化則先觸發其父類的初始化。
  4. 包含psvm(mian()方法)的那個類。
  5. 動態語言支持時,REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄。
  • 被動引用

    • 除上面五種情況之外,所有引用類的方式都不會觸發初始化,稱為被動引用。
  1. 通過子類引用父類的靜態字段,不會導致子類的初始化。
  2. 通過數組定義來引用類,不會觸發此類的初始化。該過程會對數組類進行初始化,數組類是一個由虛擬機自動生成的、直接繼承Object的子類,其中包含數組的屬性和方法,用戶只能使用public的length和clone()。
  3. 常量在編譯階段會存入調用類的常量池中,本質上並沒有直接引用到定義常量的類,因此不會觸發定義常量的類的初始化。

類加載過程

    1. 加載
      1. 通過類的全限定名來獲取定義此類的二進制字節流。
  1. 將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構。
  2. 在內存中生成一個代表這個類的java.lang.Class對象(HotSpot將其存放在方法區中),作為方法區這個類的各種數據的訪問入口。
    1. 驗證
    • 為了確保Class文件的字節類中包含的信息符合當前虛擬機的要求,並且不會危害虛擬機自身的安全。可以通過-Xverify:none關閉大部分類驗證。
  1. 文件格式驗證。確保輸入字節流能正確的解析並存儲於方法區,後面的3個驗證全部基於方法區的存儲結構進行,不會再操作字節流。
  2. 元數據驗證。對字節碼描述信息進行語義分析,確保其符合Java語法規範。(Java語法驗證)
  3. 字節碼驗證。最複雜,通過數據流和控制流分析,確定程序語義時合法的、符合邏輯的。可以通過參數關閉。(驗證指令跳轉範圍,類型轉換有效等)
  4. 符號引用驗證。將符號引用轉化為直接引用,發生在第三個階段——解析階段中發生。
    1. 準備
    • 類變量是被static修飾的變量,準備階段為類變量分配內存並設置零值(final直接設置初始值),使用的是方法區的內存。
    1. 解析
    • 將常量池內的符號引用替換為直接引用的過程。
      其中解析過程在某些情況下可以在初始化階段之後再開始,這是為了支持Java的動態綁定。
      解析動作主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄、和調用點限定符。
    1. 初始化
    • 初始化階段才真正執行類中定義的Java程序代碼,是執行類構造器 ()方法的過程。
      在準備階段,類變量已經給過零值,而在初始化階段,根據程序員通過程序制定的主觀計劃去初始化類變量和其他資源。

      • ()

        • 類構造器方法。是由編譯器自動收集類中的所有類變量的賦值動作和靜態語句塊中的的語句合併產生的。
  1. 不需要顯式調用父類構造器,JVM會保證在子類clinit執行之前,父類的clinit已經執行完成。
  2. 接口中不能使用靜態語句塊但仍可以有類變量的賦值操作。當沒有使用父接口中定義的變量時子接口的clinit不需要先執行父接口的clinit方法。接口的實現類也不會執行接口的clinit方法。
  3. 虛擬機會保證clinit在多線程環境中被正確的加鎖、同步。其他線性喚醒之後不會再進入clinit方法,同一個類加載器下,一個類型只會初始化一次。

     - <init>()
    
         - 對象構造器方法。Java對象被創建時才會進行實例化操作,對非靜態變量解析初始化。
  4. 會顯式的調用父類的init方法,對象實例化過程中對實例域的初始化操作全部在init方法中進行。

類(加載) 器

  • 類與類加載器

    • 類加載器實現類的加載動作。
      類加載器和這個類本身一同確立這個類的唯一性,每個類加載器都有獨立的類命名空間。在同一個類加載器加載的情況下才會有兩個類相等。
      相等包括類的Class對象的equals()方法、isAssignableFrom()方法、isInstance()、instanceof關鍵字。
  • 類加載器分類

    • 啟動類加載器

      • 由C++語言實現,是虛擬機的一部分。負責將JAVA_HOME/lib目錄中,或者被-Xbootclasspath參數指定的路徑,但是文件名要能被虛擬機識別,名字不符合無法被啟動類加載器加載。啟動類加載器無法被Java程序直接引用。
    • 擴展類加載器

      • 由Java語言實現,負責加載JAVA_HOME/lib/ext目錄,或者被java.ext.dirs系統變量所指定的路徑中的所有類庫,開發者可以直接使用擴展類加載器。
    • 應用程序類加載器

      • 由於這個類加載器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也稱他為系統類加載器。負責加載用戶類路徑(ClassPath)上所指定的類庫,一般情況下這個就是程序中默認的類加載器。
    • 自定義類加載器

      • 由用戶自己實現。
  1. 如果不想打破雙親委派模型,那麼只需要重寫findClass方法即可。
  2. 否則就重寫整個loadClass方法。
  • 雙親委派模型

    • 雙親委派模型要求除了頂層的啟動類加載器外,其餘的類加載器都應該有自己的父類加載器。父子不會以繼承的關係類實現,而是都是使用組合關係來服用父加載器的代碼。
      在java.lang.ClassLoader的loadClass()方法中實現。
    • 工作過程

      • 一個類加載器首先將類加載請求轉發到父類加載器,只有當父類加載器無法完成(它的搜索範圍中沒有找到所需要的類)時才嘗試自己加載
    • 好處

      • Java類隨着它的類加載器一起具備了一種帶有優先級的層次關係,從而使得基礎類庫得到同意。

四、內存分配與回收策略

Minor GC 和 Full GC

  • Minor GC

    • 發生在新生代的垃圾收集動作,因為新生代對象存活時間很短,因此Minor GC會頻繁執行,執行速度快。
    • 時機

      • Eden不足
  • Full GC

    • 發生在老年區的GC,出現Full GC時往往伴隨着Minor GC,比Minor GC慢10倍以上。
    • 時機

        1. 調用System.gc()
        • 只是建議虛擬機執行Full GC,但是虛擬機不一定真正去執行。
          不建議使用這種方式,而是讓虛擬機管理內存。
        1. 老年代空間不足
        • 常見場景就是大對象和長期存活對象進入老年代。
          盡量避免創建過大的對象以及數組,調大新生代大小,讓對象盡量咋新生代中被回收,不進入老年代。
        1. JDK1.7 之前方法區空間不足
        • 當系統中要加載的類、反射的類和常量較多時,永久代可能會被佔滿,在未配置CMS GC的情況下也會執行Full GC,如果空間仍然不夠則會拋出OOM異常。
          可採用增大方法區空間或轉為使用CMS GC。
        1. 空間分配擔保失敗
        • 發生Minor GC時分配擔保的兩個判斷失敗
        1. Concurrent Mode Failure
        • CMS GC 併發清理階段用戶線程還在執行,不斷有新的浮動垃圾產生,當預留空間不足時報Concurrent Mode Failure錯誤並觸發Full GC。

內存分配策略

    1. 對象優先在Eden分配
    • 大多數情況下,對象在新生代Eden上分配,當Eden空間不夠時,發起Minor GC,當另外一個Survivor空間不足時則將存活對象通過分配擔保機制提前轉移到老年代。
    1. 大對象直接進入老年代
    • 配置參數-XX:PretenureSizeThreshold,大於此值得對象直接在老年代分配,避免在Eden和Survivor之間的大量內存複製。
    1. 長期存活對象進入老年代
    • 虛擬機為每個對象定義了一個Age計數器,對象在Eden出生並經過Minor GC存活轉移到另一個Survivor空間中時Age++,增加到默認16則轉移到老年代。
    1. 動態對象年齡綁定
    • 虛擬機並不是永遠要求對象的年齡必須到達MaxTenuringThreshold才能晉陞老年代,如果在Survivor中相同年齡所有對象大小總和大於Survivor空間的一半,則年齡大於或等於該年齡的對象直接進入老年代。
    1. 空間分配擔保
    • 在發生Minor GC之前,虛擬機先檢查老年代最大可用的連續空間是否大於新生代的所有對象,如果條件成立,那麼Minor GC可以認為是安全的。
      可以通過HandlePromotionFailure參數設置允許冒險,此時虛擬機將與歷代晉陞到老年區對象的平均大小比較,仍小於則要進行一次Full GC。
      在JDK1.6.24之後HandlePromotionFailure已無作用,即虛擬機默認為true。

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

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

※帶您來看台北網站建置台北網頁設計,各種案例分享

徹底搞懂 netty 線程模型

編者注:Netty是Java領域有名的開源網絡庫,特點是高性能和高擴展性,因此很多流行的框架都是基於它來構建的,比如我們熟知的Dubbo、Rocketmq、Hadoop等。本文就netty線程模型展開分析討論下 : )

IO模型

  • BIO:同步阻塞IO模型;
  • NIO:基於IO多路復用技術的“非阻塞同步”IO模型。簡單來說,內核將可讀可寫事件通知應用,由應用主動發起讀寫操作;
  • AIO:非阻塞異步IO模型。簡單來說,內核將讀完成事件通知應用,讀操作由內核完成,應用只需操作數據即可;應用做異步寫操作時立即返回,內核會進行寫操作排隊並執行寫操作。

NIO和AIO不同之處在於應用是否進行真正的讀寫操作。

reactor和proactor模型

  • reactor:基於NIO技術,可讀可寫時通知應用;
  • proactor:基於AIO技術,讀完成時通知應用,寫操作應用通知內核。

netty線程模型

netty的線程模型是基於Reactor模型的。

netty單線程模型

Reactor 單線程模型,是指所有的 I/O 操作都在同一個 NIO 線程上面完成的,此時NIO線程職責包括:接收新建連接請求、讀寫操作等。

在一些小容量應用場景下,可以使用單線程模型(注意,Redis的請求處理也是單線程模型,為什麼Redis的性能會如此之高呢?因為Redis的讀寫操作基本都是內存操作,並且Redis協議比較簡潔,序列化/反序列化耗費性能更低)。但是對於高負載、大併發的應用場景卻不合適,主要原因如下:

  • 一個NIO線程同時處理成百上千的連接,性能上無法支撐,即便NIO線程的CPU負荷達到100%,也無法滿足海量消息的編碼、解碼、讀取和發送。
  • 當NIO線程負載過重之後,處理速度將變慢,這會導致大量客戶端連接超時,超時之後往往會進行重發,這更加重了NIO線程的負載,最終會導致大量消息積壓和處理超時,成為系統的性能瓶頸。
  • 可靠性問題:一旦NIO線程意外跑飛,或者進入死循環,會導致整個系統通信模塊不可用,不能接收和處理外部消息,造成節點故障。

Reactor多線程模型

Rector 多線程模型與單線程模型最大的區別就是有一組 NIO 線程來處理連接讀寫操作,一個NIO線程處理Accept。一個NIO線程可以處理多個連接事件,一個連接的事件只能屬於一個NIO線程。

在絕大多數場景下,Reactor 多線程模型可以滿足性能需求。但是,在個別特殊場景中,一個 NIO 線程負責監聽和處理所有的客戶端連接可能會存在性能問題。例如併發百萬客戶端連接,或者服務端需要對客戶端握手進行安全認證,但是認證本身非常損耗性能。在這類場景下,單獨一個 Acceptor 線程可能會存在性能不足的問題,為了解決性能問題,產生了第三種 Reactor 線程模型——主從Reactor 多線程模型。

Reactor主從多線程模型

主從 Reactor 線程模型的特點是:服務端用於接收客戶端連接的不再是一個單獨的 NIO 線程,而是一個獨立的 NIO 線程池。Acceptor 接收到客戶端 TCP連接請求並處理完成后(可能包含接入認證等),將新創建的 SocketChannel注 冊 到 I/O 線 程 池(sub reactor 線 程 池)的某個I/O線程上, 由它負責SocketChannel 的讀寫和編解碼工作。Acceptor 線程池僅僅用於客戶端的登錄、握手和安全認證,一旦鏈路建立成功,就將鏈路註冊到後端 subReactor 線程池的 I/O 線程上,由 I/O 線程負責後續的 I/O 操作。

netty線程模型思考

netty 的線程模型並不是一成不變的,它實際取決於用戶的啟動參數配置。通過設置不同的啟動參數,Netty 可以同時支持 Reactor 單線程模型、多線程模型。

為了盡可能地提升性能,Netty 在很多地方進行了無鎖化的設計,例如在 I/O 線程內部進行串行操作,避免多線程競爭導致的性能下降問題。表面上看,串行化設計似乎 CPU 利用率不高,併發程度不夠。但是,通過調整 NIO 線程池的線程參數,可以同時啟動多個串行化的線程并行運行,這種局部無鎖化的串行線程設計相比一個隊列多個工作線程的模型性能更優。(小夥伴們後續多線程併發流程可參考該類實現方案

Netty 的 NioEventLoop 讀取到消息之後,直接調用 ChannelPipeline 的fireChannelRead (Object msg)。 只要用戶不主動切換線程, 一直都是由NioEventLoop 調用用戶的 ChannelHandler,期間不進行線程切換。這種串行化處理方式避免了多線程操作導致的鎖的競爭,從性能角度看是最優的。

Netty擁有兩個NIO線程池,分別是bossGroupworkerGroup,前者處理新建連接請求,然後將新建立的連接輪詢交給workerGroup中的其中一個NioEventLoop來處理,後續該連接上的讀寫操作都是由同一個NioEventLoop來處理。注意,雖然bossGroup也能指定多個NioEventLoop(一個NioEventLoop對應一個線程),但是默認情況下只會有一個線程,因為一般情況下應用程序只會使用一個對外監聽端口。

這裏試想一下,難道不能使用多線程來監聽同一個對外端口么,即多線程epoll_wait到同一個epoll實例上?

epoll相關的主要兩個方法是epoll_wait和epoll_ctl,多線程同時操作同一個epoll實例,那麼首先需要確認epoll相關方法是否線程安全:簡單來說,epoll是通過鎖來保證線程安全的, epoll中粒度最小的自旋鎖ep->lock(spinlock)用來保護就緒的隊列, 互斥鎖ep->mtx用來保護epoll的重要數據結構紅黑樹

看到這裏,可能有的小夥伴想到了Nginx多進程針對監聽端口的處理策略,Nginx是通過accept_mutex機制來保證的。accept_mutex是nginx的(新建連接)負載均衡鎖,讓多個worker進程輪流處理與client的新連接。當某個worker進程的連接數達到worker_connections配置(單個worker進程的最大處理連接數)的最大連接數的7/8時,會大大減小獲取該worker獲取accept鎖的概率,以此實現各worker進程間的連接數的負載均衡。accept鎖默認打開,關閉它時nginx處理新建連接耗時會更短,但是worker進程之間可能連接不均衡,並且存在“驚群”問題。只有在使能accept_mutex並且當前系統不支持原子鎖時,才會用文件實現accept鎖。注意,accept_mutex加鎖失敗時不會阻塞當前線程,類似tryLock。

現代linux中,多個socker同時監聽同一個端口也是可行的,nginx 1.9.1也支持這一行為。linux 3.9以上內核支持SO_REUSEPORT選項,允許多個socker bind/listen在同一端口上。這樣,多個進程可以各自申請socker監聽同一端口,當連接事件來臨時,內核做負載均衡,喚醒監聽的其中一個進程來處理,reuseport機制有效的解決了epoll驚群問題。

再回到剛才提出的問題,java中多線程來監聽同一個對外端口,epoll方法是線程安全的,這樣就可以使用使用多線程監聽epoll_wait了么,當然是不建議這樣乾的,除了epoll的驚群問題之外,還有一個就是,一般開發中我們使用epoll設置的是LT模式(水平觸發方式,與之相對的是ET默認,前者只要連接事件未被處理就會在epoll_wait時始終觸發,後者只會在真正有事件來時在epoll_wait觸發一次),這樣的話,多線程epoll_wait時就會導致第一個線程epoll_wait之後還未處理完畢已發生的事件時,第二個線程也會epoll_wait返回,顯然這不是我們想要的,關於java nio的測試demo如下:

public class NioDemo {
    private static AtomicBoolean flag = new AtomicBoolean(true);
    public static void main(String[] args) throws Exception {
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.socket().bind(new InetSocketAddress(8080));
        // non-block io
        serverChannel.configureBlocking(false);
        Selector selector = Selector.open();
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);

        // 多線程執行
        Runnable task = () -> {
            try {
                while (true) {
                    if (selector.select(0) == 0) {
                        System.out.println("selector.select loop... " + Thread.currentThread().getName());
                        Thread.sleep(1);
                        continue;
                    }

                    if (flag.compareAndSet(true, false)) {
                        System.out.println(Thread.currentThread().getName() + " over");
                        return;
                    }

                    Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
                    while (iter.hasNext()) {
                        SelectionKey key = iter.next();

                        // accept event
                        if (key.isAcceptable()) {
                            handlerAccept(selector, key);
                        }

                        // socket event
                        if (key.isReadable()) {
                            handlerRead(key);
                        }

                        /**
                         * Selector不會自己從已選擇鍵集中移除SelectionKey實例,必須在處理完通道時手動移除。
                         * 下次該通道變成就緒時,Selector會再次將其放入已選擇鍵集中。
                         */
                        iter.remove();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        };

        List<Thread> threadList = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            Thread thread = new Thread(task);
            threadList.add(thread);
            thread.start();
        }
        for (Thread thread : threadList) {
            thread.join();
        }
        System.out.println("main end");
    }

    static void handlerAccept(Selector selector, SelectionKey key) throws Exception {
        System.out.println("coming a new client... " + Thread.currentThread().getName());
        Thread.sleep(10000);
        SocketChannel channel = ((ServerSocketChannel) key.channel()).accept();
        channel.configureBlocking(false);
        channel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
    }

    static void handlerRead(SelectionKey key) throws Exception {
        SocketChannel channel = (SocketChannel) key.channel();
        ByteBuffer buffer = (ByteBuffer) key.attachment();
        buffer.clear();

        int num = channel.read(buffer);
        if (num <= 0) {
            // error or fin
            System.out.println("close " + channel.getRemoteAddress());
            channel.close();
        } else {
            buffer.flip();
            String recv = Charset.forName("UTF-8").newDecoder().decode(buffer).toString();
            System.out.println("recv: " + recv);

            buffer = ByteBuffer.wrap(("server: " + recv).getBytes());
            channel.write(buffer);
        }
    }
}

netty線程模型實踐

(1) 時間可控的簡單業務直接在 I/O 線程上處理

時間可控的簡單業務直接在 I/O 線程上處理,如果業務非常簡單,執行時間非常短,不需要與外部網絡交互、訪問數據庫和磁盤,不需要等待其它資源,則建議直接在業務 ChannelHandler 中執行,不需要再啟業務的線程或者線程池。避免線程上下文切換,也不存在線程併發問題。

(2) 複雜和時間不可控業務建議投遞到後端業務線程池統一處理

複雜度較高或者時間不可控業務建議投遞到後端業務線程池統一處理,對於此類業務,不建議直接在業務 ChannelHandler 中啟動線程或者線程池處理,建議將不同的業務統一封裝成 Task,統一投遞到後端的業務線程池中進行處理。過多的業務ChannelHandler 會帶來開發效率和可維護性問題,不要把 Netty 當作業務容器,對於大多數複雜的業務產品,仍然需要集成或者開發自己的業務容器,做好和Netty 的架構分層。

(3) 業務線程避免直接操作 ChannelHandler

業務線程避免直接操作 ChannelHandler,對於 ChannelHandler,IO 線程和業務線程都可能會操作,因為業務通常是多線程模型,這樣就會存在多線程操作ChannelHandler。為了盡量避免多線程併發問題,建議按照 Netty 自身的做法,通過將操作封裝成獨立的 Task 由 NioEventLoop 統一執行,而不是業務線程直接操作,相關代碼如下所示:

如果你確認併發訪問的數據或者併發操作是安全的,則無需多此一舉,這個需要根據具體的業務場景進行判斷,靈活處理。

推薦閱讀

歡迎小夥伴關注【TopCoder】閱讀更多精彩好文。

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

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

※帶您來看台北網站建置台北網頁設計,各種案例分享

北汽豪擲80億元佈局新能源汽車

2015年12月28日,北京汽車集團有限公司就“北汽新能源汽車動力電池”、“北汽集團常州產業基地”兩個專案,與常州市政府簽約。

此次北汽集團擬在常州建設的兩個項目總投資80億元,其中,北汽新能源汽車動力電池專案總投資約30億元,規劃動力電池產能達到5G瓦時,同時將以滆湖低碳濕地公園培訓中心為主體,打造北汽新能源綠色商學院,其主要目的是加強新體系電池基礎研究和關鍵技術開發,推進新一代鋰離子電池的工程化和產業化,實現對動力電池產業鏈核心環節資源掌控,以支撐北汽新能源業務需求。

而北汽集團常州產業基地專案總投資50億元,總規劃年產30萬輛整車及配套零部件、物流專案,其中一期年產15萬輛SUV、MPV和輕型客車,二期重點生產新能源汽車,打造產業生態鏈。

此前,北汽集團總投資100億元的新能源汽車和總投資50億元的通用航空兩個項目已經於今年4月和10月相繼落戶常州。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

※帶您來看台北網站建置台北網頁設計,各種案例分享