環境資訊中心綜合外電;姜唯 編譯;林大利 審校
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※為什麼 USB CONNECTOR 是電子產業重要的元件?
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※台北網頁設計公司全省服務真心推薦
※想知道最厲害的網頁設計公司"嚨底家"!
※推薦評價好的iphone維修中心
![]() |
荷蘭皇家殼牌(Royal Dutch Shell plc)10月12日宣布收購電動車(EV)充電服務經營商NewMotion。依據收購條件、NewMotion將成為殼牌旗下全資子公司。
殼牌新聞稿顯示,NewMotion在英國、法國、德國以及荷蘭經營超過3萬個私人(包括家庭、企業)電動車充電點。此外,NewMotion在歐洲25個國家擁有超過5萬個公共充電點、對逾10萬張註冊充電卡提供服務。
NewMotion表示,這項收購案將可協助公司提升EV充電服務、將更多的停車空間改裝成充電站,進而提升歐洲使用者的充電體驗。
(本文內容由授權使用。圖片出處:)
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※為什麼 USB CONNECTOR 是電子產業重要的元件?
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※台北網頁設計公司全省服務真心推薦
※想知道最厲害的網頁設計公司"嚨底家"!
※推薦評價好的iphone維修中心
![]() |
全球環保意識高漲,歐洲和中國相繼宣布,未來將禁售汽柴油車。不少專家預測,石油需求即將觸頂,但是能源巨擘對此嗤之以鼻,深信石油需求將持續成長,對再生能源投資只有石油的九牛一毛。 路透社8日報導,二十年前英國石油公司(BP)看好再生能源,不只改換商標,還宣布要在十年內斥資80億美元發展綠能。不料此一大膽舉動慘敗收場,BP的太陽能事業被陸廠打到無力招架,美國風力發電事業更連賣都賣不掉。BP學到教訓,再次聚焦石油,其他油商也看在眼裡,對於綠能投資格外謹慎。 證據何在?路透訪調分析顯示,全球前五大油商,包括BP、Total、雪佛龍(Chevron)、艾克森美孚(Exxon Mobil)、荷蘭殼牌(Royal Dutch Shell),投資替代能源都只是輕描淡寫。Wood Mackenzie估計前五大油商每年投資的1,000億美元中,只有3%用於再生能源。雪佛龍執行長John Watson說,目前沒有石油需求觸頂的跡象,未來10~20年,石油需求將持續成長。 能源巨擘信心滿滿,是看準新興市場的石油需求將持續增加。艾克森美孚估計,2040年亞洲的運輸需求將使得燃料需求提高25%。BP也說,全球生產石油中,有1/5用於汽車,如果電動車真的奪取大量市佔,空運、鐵路、卡車運輸仍會拉高石油需求。油商也大力投資天然氣,算準就算電動車起飛,天然氣能用於發電,需求仍會成長。 (本文內容由授權使用。首圖來源:pixabay)
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※為什麼 USB CONNECTOR 是電子產業重要的元件?
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※台北網頁設計公司全省服務真心推薦
※想知道最厲害的網頁設計公司“嚨底家”!
※推薦評價好的iphone維修中心
※聚甘新
在上一篇中,我們提到了如何創建一個UnitOfWork並通過ActionFilter設置啟用。這一篇我們將簡單介紹一下ActionFilter以及如何利用ActionFilter,順便補齊一下上一篇的工具類。
ActionFilter全稱是ActionFilterAttribute,我們根據微軟的命名規範可以看出這是一個特性類,看一下它的聲明:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public abstract class ActionFilterAttribute : Attribute, IActionFilter, IFilterMetadata, IAsyncActionFilter, IAsyncResultFilter, IOrderedFilter, IResultFilter
這是一個允許標註在類和方法上的特性類,允許多個標記,標註之後子類會繼承父類的特性。然後,這個類是一個抽象類,所以我們可以通過繼承ActionFilterAttribute來編寫自己的ActionFilter。
對於一個ActionFilter而言,最重要的是它的四個方法:
public virtual void OnActionExecuted(ActionExecutedContext context);
public virtual void OnActionExecuting(ActionExecutingContext context);
public virtual void OnResultExecuted(ResultExecutedContext context);
public virtual void OnResultExecuting(ResultExecutingContext context);
上圖是這四個方法在一次請求中執行的順序。在一次請求真正執行之前,想要攔截這個請求,應該使用OnActionExecuting。
為什麼單獨說這個呢?因為這個方法的出鏡率很高,大多數時候都會使用這個方法進行請求過濾。
我們來簡單介紹一下,四個方法中的四種上下文類型,看一看裏面有哪些我們可以利用的方法:
這是一個Action執行前的上下文,表示Action並未開始執行,但是已經獲取到了控制器實例:
public class ActionExecutingContext : FilterContext
{
public virtual IDictionary<string, object> ActionArguments { get; }
public virtual object Controller { get; }
public virtual IActionResult Result { get; set; }
}
ActionExecutingContext繼承自FilterContext,我們暫且不關注它的父類,只看一下它自己的屬性。
ActionExecutedContext 表示Action執行完成后的上下文,這時候Action已經執行完成,我們可以通過這個獲取Action執行結果:
public class ActionExecutedContext : FilterContext
{
public virtual bool Canceled { get; set; }
public virtual object Controller { get; }
public virtual Exception Exception { get; set; }
public virtual ExceptionDispatchInfo ExceptionDispatchInfo { get; set; }
public virtual bool ExceptionHandled { get; set; }
public virtual IActionResult Result { get; set; }
}
同樣,繼承自FilterContext,暫且忽略。
這是在Result渲染之前執行的上下文,這時候Action已經執行完畢,正準備渲染Result:
public class ResultExecutingContext : FilterContext
{
public virtual bool Cancel { get; set; }
public virtual object Controller { get; }
public virtual IActionResult Result { get; set; }
}
Result已經執行完成了,獲取執行結果上下文:
public class ResultExecutedContext : FilterContext
{
public virtual bool Canceled { get; set; }
public virtual object Controller { get; }
public virtual Exception Exception { get; set; }
public virtual ExceptionDispatchInfo ExceptionDispatchInfo { get; set; }
public virtual bool ExceptionHandled { get; set; }
public virtual IActionResult Result { get; }
}
這個類與 ActionExecutedContext類似,就不做介紹了。
在上面的四個上下文都繼承自 FilterContext,那麼我們來看一下FilterContext中有哪些屬性或者方法:
public abstract class FilterContext : ActionContext
{
public virtual IList<IFilterMetadata> Filters { get; }
public TMetadata FindEffectivePolicy<TMetadata>() where TMetadata : IFilterMetadata;
}
可以看到FilterContext繼承了另一個ActionContext的類。小夥伴們應該對這個類要有一定的概念,這個類是Action的上下文類。它完整存在於一個Action的生命周期,所以有時候可以通過ActionContext進行Action級的數據傳遞(不推薦)。
那麼,繼續讓我們回過頭來看看ActionContext里有什麼:
public class ActionContext
{
public ActionDescriptor ActionDescriptor { get; set; }
public HttpContext HttpContext { get; set; }
public ModelStateDictionary ModelState { get; }
public RouteData RouteData { get; set; }
}
在《【asp.net core 系列】9 實戰之 UnitOfWork以及自定義代碼生成》也就是上一篇中,介紹到了ActionFilter與普通特性類一致,可以通過標註控制器然後啟用該ActionFilter。
因為大多數情況下,一個ActionFilter並不會僅僅局限於一個控制器,而是應用於多個控制器。所以這時候,我們通常會設置一個基礎控制器,在這個控制器上進行標註,然後讓子類繼承這個控制器。通過這種方式來實現一次聲明多次使用。
當然,在asp.net core 中添加了另外的一種使用ActionFilter的方式,Setup.cs中
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
}
默認是這樣的,我們可以通過設置參數來添加一個全局應用的Filter,例如說我們上一篇中創建的 UnitOfWorkFilterAttribute:
services.AddControllersWithViews(options=>
{
options.Filters.Add<UnitOfWorkFilterAttribute>();
});
通過這種方式可以啟用一個全局ActionFilter。如果需要使用asp.net core的默認依賴注入可以使用 AddService進行配置。(依賴注入的內容在後續會講解)。
繼續上一篇遺留的內容:
public static void CreateEntityTypeConfig(Type type)
{
var targetNamespace = type.Namespace.Replace("Data.Models", "");
if (targetNamespace.StartsWith("."))
{
targetNamespace = targetNamespace.Remove(0);
}
var targetDir = Path.Combine(new[] { CurrentDirect, "Domain.Implements", "EntityConfigures" }.Concat(
targetNamespace.Split('.')).ToArray());
if (!Directory.Exists(targetDir))
{
Directory.CreateDirectory(targetDir);
}
var baseName = type.Name.Replace("Entity", "");
if (!string.IsNullOrEmpty(targetNamespace))
{
targetNamespace = $".{targetNamespace}";
}
var file = $"using {type.Namespace};" +
$"\r\nusing Microsoft.EntityFrameworkCore;" +
$"\r\nusing Microsoft.EntityFrameworkCore.Metadata.Builders;" +
$"\r\nnamespace Domain.Implements.EntityConfigures{targetNamespace}" +
"\r\n{" +
$"\r\n\tpublic class {baseName}Config : IEntityTypeConfiguration<{type.Name}>" +
"\r\n\t{" +
"\r\n\t\tpublic void Configure(EntityTypeBuilder<SysUser> builder)" +
"\r\n\t\t{" +
$"\r\n\t\t\tbuilder.ToTable(\"{baseName}\");" +
$"\r\n\t\t\tbuilder.HasKey(p => p.Id);" +
"\r\n\t\t}\r\n\t}\r\n}";
File.WriteAllText(Path.Combine(targetDir, $"{baseName}Config.cs"), file);
}
工具類其實本質上就是一次文件寫入的方法,本身沒什麼難度。
不過,這裏還有有個小問題,每次調用都會覆蓋原有的文件,還有就是這裏面有很多可以優化的地方,小夥伴們可以自己試試去優化一下,讓代碼更好看一點。
到目前為止,實戰系列也有了幾篇,很多小夥伴問我能提供一下源碼嗎?當然,能呀。不過不是現在,容我留個謎底。當主要框架功能完成之後,我就會給小夥伴們發代碼的。
其實也是因為現在還沒個完整的,開放給小夥伴們也沒啥意義。當然了,跟着一塊敲,也是能實現的哈。關鍵地方的代碼都有。
更多內容煩請關注我的博客《高先生小屋》
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※為什麼 USB CONNECTOR 是電子產業重要的元件?
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※台北網頁設計公司全省服務真心推薦
※想知道最厲害的網頁設計公司“嚨底家”!
※推薦評價好的iphone維修中心
※聚甘新
說明
cute-cnblogs 可愛的博客園樣式美化、自定義博客園樣式 二期樣式已經編寫完畢了,如果說 一期樣式 給人的感覺是簡潔清爽的小嬰兒的話,那麼 二期樣式 就是一個有自己小個性(花樣)的小朋友了~
與一期一樣,需要文件的可以來 github ,喜歡我寫的樣式可以幫我點個 star 喔 ღゝ◡╹)ノ
(PS:有什麼問題也可以留言到 github issues 中喔~)
好了,讓我們擼起袖子開始更換二期樣式吧~
ღゝ◡╹)ノ 麋鹿魯喲的博客園
可愛的博客園樣式美化、自定義博客園樣式 ღゝ◡╹)ノ
博客皮膚選定: 博客園 Custom 標準模板(與一期不同喔)
請按照順序進行操作喔~
首先記得申請JS權限
其次博客皮膚選擇 Custom
在此需要獲取數據(不然點擊頭像的關注會失敗)
找一個沒有登陸的瀏覽器訪問自己的博客園,F12彈出窗口,找到 +加關注,複製follow括號里的內容,暫且先存在一個地方
勾選禁用模板默認CSS
<p style="text-align: center;">歡迎來到我的友鏈小屋</p>
<div class="friendsbox">
<div id="app">
<h6 style="text-align: center; color: #2daebf;">展示本站所有友情站點,排列不分先後,均勻打亂算法隨機渲染的喔!</h6>
<div class="unionbox-box" style="display: flex; flex-wrap: wrap; justify-content: space-between; margin-bottom: 1.5rem; margin-top: 1.5rem;"> </div>
<hr style="position: relative; margin: 1rem 0; border: 1px dashed #9eabb3; width: calc(100%); height: 0;" />
<h2 style="text-align: center;">友鏈信息</h2>
<h5 style="text-align: left; line-height: 30px;">博客名稱:<a href="javascript:void(0)">麋鹿魯喲</a><br />博客網址:<a href="javascript:void(0)">https://www.cnblogs.com/miluluyo/</a><br />博客頭像:<a href="javascript:void(0)">https://pic.cnblogs.com/avatar/1273193/20190806180831.png</a><br />博客介紹:<a href="javascript:void(0)">大道至簡,知易行難。</a></h5>
<h2 style="text-align: center;">join us</h2>
<h5 style="text-align: center; color: #2daebf;">如需友鏈,請添加微信(s978761)告知,格式如下</h5>
<table class="table friendstable" style="margin: 0 auto;">
<tbody>
<tr><th><strong>字段</strong></th><th><strong>字義</strong></th></tr>
</tbody>
</table>
</div>
</div>
複製 https://blog-static.cnblogs.com/files/miluluyo/cute-cnblogs2.css 的文件內容放到 頁面定製CSS代碼 區域
<link href="https://blog-static.cnblogs.com/files/miluluyo/tippy.min.css" rel="stylesheet">
<script src="https://blog-static.cnblogs.com/files/miluluyo/jquery2.min.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/tippy.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/milusidebar.js"></script>
<script>
milusidebar({
'names' : '麋鹿魯喲',/*你的博客園名吶*/
'notice' : '<b>溫馨提示</b><span><a href="https://github.com/miluluyo/cute-cnblogs" target="_black">cute-cnblogs</a> 樣式已開源</span><b style="margin-top: 3px;"><a style="font-size:10px" href="https://www.cnblogs.com/IsAlpaca/" target="_black">查看一期樣式</a></b>',/*裏面文字自己可以更改喔*/
'headerUrl' : 'https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200519075219notice5.png',/*這個是公告欄的背景圖啦,我覺得這個可愛,如果你有更好看的可以自行更改喔*/
'follow' : 'a1e76459-101d-47af-a8b6-08d523685c8c', /*還記的開始讓你複製follow括號里的內容嗎,對,就放到這裏就好啦*/
'sidebarInfo' : [[
{'icon':'#icon-github1','url':'https://github.com/miluluyo','title':'github'},
{'icon':'#icon-weixin','url':'','title':'微信','classname':'popper_weixin','click':false},
{'icon':'#icon-QQ','url':'http://wpa.qq.com/msgrd?v=3&uin=978761587&site=qq&menu=yes','title':'QQ'},
{'icon':'#icon-juejin','url':'https://juejin.im/user/5d18adce5188256e98090e33','title':'掘金'}
],[
{'icon':'#icon-weibobangding','url':'https://www.weibo.com/6001406082/profile?topnav=1&wvr=6','title':'微博'},
{'icon':'#icon-csdn','url':'https://blog.csdn.net/qq_39394518','title':'CSDN'},
{'icon':'#icon-bilibili','url':'https://space.bilibili.com/100007925','title':'bilibili'},
{'icon':'#icon-yuquemianlogo','url':'https://www.yuque.com/miluluyo','title':'語雀'}
]],/*這個模塊是個人信息內那些小圖標們,別忘記更改喔,具體參數,可以參考下面的表格喔*/
'signature':'靡不有初 鮮克有終',/*來一句你自己喜歡的句子吧*/
'popper_weixin':'<div class="popper_box"><p><b>很高興認識你鴨~ (づ。◕ᴗᴗ◕。)づ</b> </p><div class="popper_box_con"><div class="popper_box_con_li"><img src="https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200614064005qrcode.jpg" alt="">公眾號:麋鹿魯喲</div><div class="popper_box_con_li"><img src="https://images.cnblogs.com/cnblogs_com/miluluyo/1493340/t_wxh.jpg" alt="">微信號:s978761</div></div><p>(加我記得備註 博客園 喔)</div>',/*這裡是微信圖標的彈窗內容,可以自行更改內容喔*/
'portrait':'https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200515061851tx.jpg'
})/*這個是頭像圖片喔,你可以上傳到相冊里,然後F12獲取,或者使用博客園的那個鏈接也可以的撒~*/
</script>
| 名稱 | 類型 | 默認值/實例 | 描述 |
|---|---|---|---|
| names | 字符串 | ‘麋鹿魯喲’ | 博客園名稱 |
| notice | 字符串 | ‘<b>溫馨提示</b><span><a href=”https://github.com/miluluyo/cute-cnblogs” target=”_black”>cute-cnblogs</a> 樣式已開源</span><b style=”margin-top: 3px;”><a style=”font-size:10px” href=”https://www.cnblogs.com/IsAlpaca/” target=”_black”>查看一期樣式</a></b>’ | 公告內容 |
| headerUrl | 字符串 | ‘https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200519075219notice5.png’ | 公告欄的背景圖 |
| follow | 字符串 | ‘a1e76459-101d-47af-a8b6-08d523685c8c’ | 複製follow括號里的內容,這是關注的那個碼 |
| sidebarInfo | 數組 | [[ {‘icon’:’#icon-github1′,’url’:’https://github.com/miluluyo’,’title’:’github’}, {‘icon’:’#icon-weixin’,’url’:”,’title’:’微信’,’classname’:’popper_weixin’,’click’:false}, {‘icon’:’#icon-QQ’,’url’:’http://wpa.qq.com/msgrd?v=3&uin=978761587&site=qq&menu=yes’,’title’:’QQ’}, {‘icon’:’#icon-juejin’,’url’:’https://juejin.im/user/5d18adce5188256e98090e33′,’title’:’掘金’} ],[ {‘icon’:’#icon-weibobangding’,’url’:’https://www.weibo.com/6001406082/profile?topnav=1&wvr=6′,’title’:’微博’}, {‘icon’:’#icon-csdn’,’url’:’https://blog.csdn.net/qq_39394518′,’title’:’CSDN’}, {‘icon’:’#icon-bilibili’,’url’:’https://space.bilibili.com/100007925′,’title’:’bilibili’}, {‘icon’:’#icon-yuquemianlogo’,’url’:’https://www.yuque.com/miluluyo’,’title’:’語雀’} ]] | 個人信息內那些小圖標們 icon 圖標 url 跳轉鏈接 title 提示名字 classname 要添加的class名 click 是否允許點擊跳轉 本框架有擴展的icon,文件在 github 中的 icon 文件夾內,可以下載去查看 |
| signature | 字符串 | ‘靡不有初 鮮克有終’ | 個人信息簽名 (寫一句喜歡的話吧) |
| popper_weixin | 字符串 | ‘< div class=”popper_box”>< p>< b>很高興認識你鴨~ (づ。◕ᴗᴗ◕。)づ< /b> < /p>< div class=”popper_box_con”>< div class=”popper_box_con_li”>< img src=”https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200614064005qrcode.jpg” alt=””>公眾號:麋鹿魯喲< /div>< div class=”popper_box_con_li”>< img src=”https://images.cnblogs.com/cnblogs_com/miluluyo/1493340/t_wxh.jpg” alt=””>微信號:s978761< /div>< /div>< p>(加我記得備註 博客園 喔)< /div>’ | 微信焦點彈窗,內容可自行更改,可以放一些公眾號啊啥的~ |
| portrait | 字符串 | ‘https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200515061851tx.jpg’ | 頭像圖片路徑 |
<div id="set_btn_box">
<div class="set_btn fly_top fadeIn animated">
<svg class="icon" aria-hidden="true"><use xlink:href="#icon-zhiding"></use></svg>
</div>
<div class="set_btn article_icon_btn catalogue_btn">
<svg class="icon" aria-hidden="true" style="color:#97A1A7"><use xlink:href="#icon-dagang"></use></svg>
</div>
<div class="set_btn article_icon_btn comment">
<a href="#comment_form_container"><svg class="icon" aria-hidden="true" style="color:#97A1A7"><use xlink:href="#icon-linedesign-01"></use></svg></a>
</div>
<div class="set_btn skin_btn">
<svg class="icon" aria-hidden="true" style="color:#97A1A7"><use xlink:href="#icon-pifu"></use></svg>
</div>
<div class="set_btn gratuity">
<svg class="icon" aria-hidden="true" style="color:#97A1A7"><use xlink:href="#icon-dashang"></use></svg>
</div>
<div class="set_btn article_icon_btn artice_recommend">
<svg class="icon" aria-hidden="true" style="color:#97A1A7"><use xlink:href="#icon-tuijian2"></use></svg>
</div>
<canvas id="thumsCanvas" width="200" height="400" style="width:100px;height:200px"></canvas>
<div class="set_btn catalogue">
<svg class="icon" aria-hidden="true" style="color:#97A1A7"><use xlink:href="#icon-cebianlan-"></use></svg>
</div>
</div>
<script src='https://blog-static.cnblogs.com/files/miluluyo/canvas2.js'></script>
<!--
<link href="//files.cnblogs.com/files/linianhui/lnh.cnblogs.css" rel="stylesheet"/>-->
頁腳Html代碼
<style id="ceshicss">
@media (max-width: 767px){
#set_btn_box {width: 100vw;left: 0;right: 0;bottom: 0;background: hsla(0,0%,100%,.6);height: 49px;display: flex;justify-content: space-between;align-items: center;padding: 12px 40px;border-top: 1px solid #e8e8e8;box-sizing: border-box;}
.set_btn {margin-top: 0;}
.set_btn.fly_top.fadeIn.animated {position: absolute;right: 10px;bottom: 60px;}
.container{bottom:50px}}
#mainContent{width:90%}
</style>
<link href="https://blog-static.cnblogs.com/files/miluluyo/tippy.min.css" rel="stylesheet">
<script src="https://unpkg.com/@popperjs/core@2.4.2/dist/umd/popper.min.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/tippy.js"></script>
<link rel='stylesheet' href='https://cdn.bootcss.com/animate.css/3.7.2/animate.min.css'>
<script src="https://at.alicdn.com/t/font_1825850_klax1ao4o6.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/three.min.js"></script>
<script src='https://blog-static.cnblogs.com/files/miluluyo/star.js'></script>
<link rel="stylesheet" href="https://blog-static.cnblogs.com/files/miluluyo/OwO.min.css" />
<script src="https://blog-static.cnblogs.com/files/miluluyo/OwO2.min.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/cute-cnblogs2.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/monitoring2.js"></script>
<script>
miluframe({
Youself:'https://www.cnblogs.com/miluluyo/', /*個人的博客園鏈接*/
/*博客園導航信息*/
custom:[{
name:'首頁',
link:'https://www.cnblogs.com/miluluyo/',
istarget:false
},{
name:'聯繫',
link:'https://msg.cnblogs.com/send/%E9%BA%8B%E9%B9%BF%E9%B2%81%E5%93%9F',
istarget:true
},{
name:'技能樹',
link:'https://miluluyo.github.io/',
istarget:true
},{
name:'留言板',
link:'https://www.cnblogs.com/miluluyo/p/11578505.html',
istarget:false
},{
name:'相冊',
link:'https://www.cnblogs.com/miluluyo/gallery.html',
istarget:false
},{
name:'友鏈',
link:'https://www.cnblogs.com/miluluyo/p/11633791.html',
istarget:false
},{
name:'維護',
link:'https://www.cnblogs.com/miluluyo/p/12092009.html',
istarget:false
},{
name:'投喂',
link:'https://www.cnblogs.com/miluluyo/p/gratuity.html',
istarget:false
},{
name:'管理',
link:'https://i.cnblogs.com/',
istarget:true
}],
/*向別人展示自己的友鏈信息*/
resume:{
"name":"麋鹿魯喲",
"link":"https://www.cnblogs.com/miluluyo/",
"headurl":"https://images.cnblogs.com/cnblogs_com/elkyo/1558759/o_o_my.jpg",
"introduction":"大道至簡,知易行難。"
},
/*友鏈信息*/
unionbox:[{
"name":"麋鹿魯喲",
"introduction":"生活是沒有標準答案的。",
"url":"https://www.cnblogs.com/miluluyo",
"headurl":"https://images.cnblogs.com/cnblogs_com/elkyo/1558759/o_o_my.jpg"
},{
"name":"麋鹿魯喲的技能樹",
"introduction":"大道至簡,知易行難。",
"url":"https://miluluyo.github.io/",
"headurl":"https://images.cnblogs.com/cnblogs_com/elkyo/1558759/o_o_my.jpg"
}],
/*友鏈表格頭信息,這個可以忽略*/
details:[{
field: 'name',
literal: '昵稱',
},{
field: 'introduction',
literal: '標語',
},{
field: 'url',
literal: '鏈接地址',
},{
field: 'headurl',
literal: '頭像地址',
}],
/*瀏覽器頂部小圖標*/
logoimg:'https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200519070633f12.png',
/*文章頁面標題前的圖標,此處圖標有擴展,下面會提到圖標*/
cuteicon:['icon-caomei','icon-boluo','icon-huolongguo','icon-chengzi','icon-hamigua','icon-lizhi','icon-mangguo','icon-liulian','icon-lizi','icon-lanmei','icon-longyan','icon-shanzhu','icon-pingguo','icon-mihoutao','icon-niuyouguo','icon-xigua','icon-putao','icon-xiangjiao','icon-ningmeng','icon-yingtao','icon-taozi','icon-shiliu','icon-ximei','icon-shizi'],
/*讚賞,若true則显示此按鈕,false則不显示*/
isGratuity:true,
/*讚賞按鈕焦點显示讚賞內容,內容可自行更改*/
gratuity:'<div class="popper_box"><p><b>要請我喝奶茶嗎 (づ。◕ᴗᴗ◕。)づ</b> </p><div class="popper_box_con"><div class="popper_box_con_li"><img src="https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200521053817wx.png" alt="">微信掃碼</div><div class="popper_box_con_li"><img src="https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200521053827zfb.png" >支付寶掃碼</div></div><p><b>留下一句你覺得很勵志與美的話給我吧~</b> <b><a href="https://www.cnblogs.com/miluluyo/p/12930946.html">GO</a></b></div>'
})
</script>
<!-- 點贊 -->
<canvas width="1777" height="841" style="position: fixed; left: 0px; top: 0px; z-index: 2147483647; pointer-events: none;"></canvas><script src="https://blog-static.cnblogs.com/files/miluluyo/mouse-click.js"></script>
<!-- 以下內容是否添加你隨意 -->
<script>
/*在文章頁面添加古詩詞*/
$("#navigator").after('<div class="poem-wrap"><div class="poem-border poem-left"></div><div class="poem-border poem-right"></div><h1>念兩句詩</h1><div id="poem_sentence"></div><div id="poem_info"></div></div>')
</script>
<script src="https://sdk.jinrishici.com/v2/browser/jinrishici.js" charset="utf-8"></script>
<script type="text/javascript">
jinrishici.load(function(result) {
var sentence = document.querySelector("#poem_sentence")
var info = document.querySelector("#poem_info")
sentence.innerHTML = result.data.content
info.innerHTML = '【' + result.data.origin.dynasty + '】' + result.data.origin.author + '《' + result.data.origin.title + '》'
});
</script>
<script type="text/javascript">
/* 鼠標特效,我覺得太花哨了就註釋了,喜歡的自己打開註釋就可以 */
/*var a_idx = 0;
jQuery(document).ready(function($) {
$("body").click(function(e) {
var a = new Array("去活出你自己。","今天的好計劃勝過明天的完美計劃。","不要輕言放棄,否則對不起自己。","緊要關頭不放棄,絕望就會變成希望。","如果不能改變結果,那就完善過程。","好好活就是干有意義的事,有意義的事就是好好活!","你真正是誰並不重要,重要的是你的所做所為。","你不想為你的信仰冒一下險嗎?難道想等你老了,再後悔莫及嗎?","有些鳥兒是關不住的,它的每一根羽毛都閃耀着自由的光輝。","決定我們成為什麼樣人的,不是我們的能力,而是我們的選擇。","掉在水裡你不會淹死,呆在水裡你才會淹死,你只有游,不停的往前游。","有些路,只能一個人走。","希望你眼眸有星辰,心中有山海。","從此以夢為馬,不負韶華。","人的成就和差異決定於其業餘時間。","佛不要你皈依,佛要你歡喜。","ダーリンのこと 大好きだよ","小貓在午睡時,地球在轉。","我,混世大魔王,申請做你的小熊軟糖。","決定好啦,要暗暗努力。","吶,做人呢最緊要開心。","好想邀請你一起去雲朵上打呼嚕呀。","永遠年輕,永遠熱淚盈眶。","我生來平庸,也生來驕傲。","我走得很慢,但我從不後退。","人間不正經生活手冊。","我是可愛的小姑娘,你是可愛。","數學里,有個溫柔霸道的詞,有且僅有。","吧唧一口,吃掉難過。","你頭髮亂了哦。","健康可愛,沒有眼袋。","日月星辰之外,你是第四種難得。","你是否成為了了不起的成年人?","大家都是第一次做人。","何事喧嘩?!","人間有味是清歡。","你笑起來真像好天氣。","風填詞半句,雪斟酒一壺。","除了自渡,他人愛莫能助。","昨日種種,皆成今我。","一夢入混沌 明月撞星辰","保持獨立 適當擁有","謝謝你出現 這一生我很喜歡","做自己就好了 我會喜歡你的","太嚴肅的話,是沒辦法在人間尋歡作樂的","願你餘生可隨遇而安,步步慢。","黃瓜在於拍,人生在於嗨","奇變偶不變,符號看象限。","從來如此,便對么?","今天我這兒的太陽,正好適合曬鈣 你呢","未來可期,萬事勝意。","星光不問趕路人 時光不負有心人","我當然不會試圖摘月,我要月亮奔我而來","女生要修鍊成的五樣東西: 揚在臉上的自信,長在心底的善良, 融進血里的骨氣,刻進命里的堅強,深到骨子里的教養","燕去燕歸,滄海桑田。縱此生不見,平安惟願","我想認識你 趁風不注意","我一直想從你的窗子里看月亮","長大應該是變溫柔,對全世界都溫柔。","別在深夜做任何決定","山中何事,松花釀酒,春水煎茶。","桃李春風一杯酒,江湖夜雨十年燈。","欲買桂花同載酒,終不似,少年游。");
var le = Math.ceil(Math.random()*a.length);
var $i = $("<span></span>").text(a[le]);/*a[a_idx]*/
/*a_idx = (a_idx + 1) % a.length;
var x = e.pageX,
y = e.pageY;
$i.css({
"z-index": 999999999999999999999999999999999999999999999999999999999999999999999,
"top": y - 20,
"left": x,
"position": "absolute",
"font-weight": "bold",
"color": "rgb("+~~(255*Math.random())+","+~~(255*Math.random())+","+~~(255*Math.random())+")"
});
$("body").append($i);
$i.animate({
"top": y - 180,
"opacity": 0
},
2000,
function() {
$i.remove();
});
});
});*/
</script>
<!--音樂,只在PC端寬度>1000px時显示-->
<link rel="stylesheet" href="https://blog-static.cnblogs.com/files/miluluyo/APlayer.min.css">
<div id="player" class="aplayer aplayer-withlist aplayer-fixed" data-id="3116636104" data-server="netease" data-type="playlist" data-order="random" data-fixed="true" data-listfolded="true" data-theme="#2D8CF0"></div>
<script src="https://blog-static.cnblogs.com/files/miluluyo/APlayer.min.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/Meting.min.js"></script>
<!--貓,只在PC端显示,移動端不加載了,因為會卡頓頁面-->
<script src="https://eqcn.ajz.miesnfu.com/wp-content/plugins/wp-3d-pony/live2dw/lib/L2Dwidget.min.js"></script>
<script>
var mobile_flag = isMobile();
if(mobile_flag){
//console.info("移動端")
}else{
//console.info("PC端")
L2Dwidget.init({
"model": {
"jsonPath": "https://unpkg.com/live2d-widget-model-hijiki/assets/hijiki.model.json",
"scale": 1
},
"display": {
"position": "left",
"width": 100,
"height": 200,
"hOffset": 70,
"vOffset": 0
},
"mobile": {
"show": true,
"scale": 0.5
},
"react": {
"opacityDefault": 0.7,
"opacityOnHover": 0.2
}
});
window.onload = function(){
$("#live2dcanvas").attr("style","position: fixed; opacity: 0.7; left: 70px; bottom: 0px; z-index: 1; pointer-events: none;")
}
}
</script>
<script>
/*記錄訪問數據,我用了兩個,一個是這個在 https://clustrmaps.com/ 網站,另一個是 https://www.51.la/ 這個網站*/
/*
第一種:https://clustrmaps.com/
註冊賬號,添加自己博客園的鏈接,選擇自定義Customize,
選擇 Image based (basic version for websites that don't support javascript),調整到你喜歡的樣式,然後複製
:這個我插入在了側邊欄的最底部,把生成的代碼粘貼到append內,這就完事了
*/
$('#sideBarMain').append('')
/*
第二種:https://www.51.la/
註冊賬號,點控制台,添加統計ID,統計圖標显示我是不显示的
這個目前插入的js好像報錯,我的是很早之前生成的,還能用,因此還是推薦用第一種吧
別的地方也有這種很多統計訪問數據的,可以自己找找看呢
*/
</script>
| 名稱 | 類型 | 默認值/實例 | 描述 |
|---|---|---|---|
| Youself | 字符串 | https://www.cnblogs.com/miluluyo/ | 個人博客園首鏈接 |
| custom | 數組 | [{ name:’首頁’, link:’https://www.cnblogs.com/miluluyo/’, istarget:false },{ name:’聯繫’, link:’https://msg.cnblogs.com/send/%E9%BA%8B%E9%B9%BF%E9%B2%81%E5%93%9F’, istarget:true },{ name:’技能樹’, link:’https://miluluyo.github.io/’, istarget:true },{ name:’留言板’, link:’https://www.cnblogs.com/miluluyo/p/11578505.html’, istarget:false },{ name:’相冊’, link:’https://www.cnblogs.com/miluluyo/gallery.html’, istarget:false },{ name:’友鏈’, link:’https://www.cnblogs.com/miluluyo/p/11633791.html’, istarget:false },{ name:’維護’, link:’https://www.cnblogs.com/miluluyo/p/12092009.html’, istarget:false },{ name:’投喂’, link:’https://www.cnblogs.com/miluluyo/p/gratuity.html’, istarget:false },{ name:’管理’, link:’https://i.cnblogs.com/’, istarget:true }] | 導航信息 name 導航名 link 導航鏈接 istarget true跳轉到新頁面上,false當前頁面打開 |
| resume | 對象 | { “name”:”麋鹿魯喲”, “link”:”https://www.cnblogs.com/miluluyo/”, “headurl”:”https://images.cnblogs.com/cnblogs_com/ elkyo/1558759/o_o_my.jpg”, “introduction”:”大道至簡,知易行難。” } |
自己的友鏈信息 name 導航名 link 導航鏈接 headurl 頭像 introduction 語錄 |
| unionbox | 數組 | [{ “name”:”麋鹿魯喲”, “introduction”:”生活是沒有標準答案的。”, “url”:”https://www.cnblogs.com/miluluyo”, “headurl”:”https://images.cnblogs.com/cnblogs_com/ elkyo/1558759/o_o_my.jpg” },{ “name”:”麋鹿魯喲的技能樹”, “introduction”:”大道至簡,知易行難。”, “url”:”https://miluluyo.github.io/”, “headurl”:”https://images.cnblogs.com/cnblogs_com/ elkyo/1558759/o_o_my.jpg” }] |
友鏈數組 name 昵稱 introduction 標語 url 鏈接地址 headurl 頭像地址 |
| clicktext | 數組 | [{ field: ‘name’, literal: ‘昵稱’, },{ field: ‘introduction’, literal: ‘標語’, },{ field: ‘url’, literal: ‘鏈接地址’, },{ field: ‘headurl’, literal: ‘頭像地址’, }] | 友鏈表格頭信息,這項可以忽略掉 |
| logoimg | 字符串 | ‘https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200519070633f12.png’ | 瀏覽器頂部小圖標 |
| cuteicon | 數組 | [‘icon-caomei’,’icon-boluo’,’icon-huolongguo’,’icon-chengzi’,’icon-hamigua’,’icon-lizhi’,’icon-mangguo’,’icon-liulian’,’icon-lizi’,’icon-lanmei’,’icon-longyan’,’icon-shanzhu’,’icon-pingguo’,’icon-mihoutao’,’icon-niuyouguo’,’icon-xigua’,’icon-putao’,’icon-xiangjiao’,’icon-ningmeng’,’icon-yingtao’,’icon-taozi’,’icon-shiliu’,’icon-ximei’,’icon-shizi’] | 文章頁面標題前的圖標,此處圖標我只放入了一些水果的icon,不過可以自己引入文件進行修改名字添加自己想加的,本框架有擴展的icon,文件在 github 中的 icon 文件夾內,可以下載去查看 |
| gratuity | 字符串 | ‘<div class=”popper_box”><p><b>要請我喝奶茶嗎 (づ。◕ᴗᴗ◕。)づ</b> </p><div class=”popper_box_con”><div class=”popper_box_con_li”><img src=”https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200521053817wx.png” alt=””>微信掃碼</div><div class=”popper_box_con_li”><img src=”https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200521053827zfb.png” >支付寶掃碼</div></div><p><b>留下一句你覺得很勵志與美的話給我吧~</b> <b><a href=”https://www.cnblogs.com/miluluyo/p/12930946.html”>GO</a></b></div>’ | 讚賞按鈕焦點显示讚賞內容,內容可自行更改 |
| isGratuity | 布爾值 | true | 默認true,若true則显示此按鈕,false則不显示 |
當前框架使用了一張圖片,也可以自己進行更換成隨機圖片API
在css樣式中
#blogTitle{background:url(https://images.cnblogs.com/cnblogs_com/miluluyo/1764887/o_20051406472117.jpg) center center / cover no-repeat #222;overflow:hidden;width:100%;height:40vh;max-height:40vh;box-shadow:0 1px 2px rgba(150,150,150,.7); /*搜索這個 更換 background: url() 里的鏈接 即可*/
更多內容請查看 cute-cnblogs 自定義番外篇
(PS:可以使用番外篇里的隨機圖片API喔~)
如果您喜歡這裏,感覺對你有幫助,並且有多餘的軟妹幣的話,不妨投喂一顆糖喔~
<(▰˘◡˘▰)> 謝謝老闆~
微信掃碼
支付寶掃碼
讚賞的時候,留下一句你覺得很勵志與美的話給我吧~
(也可以加一個博客園給我喔,會添加在名字的旁邊喔~點擊可以跳轉~ 例如:去瞧瞧都有誰讚賞了
為了響應大家的號召,方便大家技術交流,之前建立了一個微信群,如果大家有需要可以掃碼(或者搜我微信號:s978761)加我好友,我邀請你加入~本群是一個純交流學習工作的群,不準發布廣告、營銷相關的信息!對了,加我記得備註上:博客園+名稱 加群 喔~
微信號:s978761
公眾號:麋鹿魯喲
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※為什麼 USB CONNECTOR 是電子產業重要的元件?
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※台北網頁設計公司全省服務真心推薦
※想知道最厲害的網頁設計公司“嚨底家”!
※推薦評價好的iphone維修中心
※聚甘新
![]() |
美國電動車商特斯拉(Tesla)於10月26日發表今年第三季財報,以淨營收2,190萬美元的成果翻紅,EPS相當於每股0.14美元。全球電動車市場蓬勃,且Model 3車款的訂單爆量,市況前景佳,也帶動了上游的供應鏈興盛。為特斯拉車用電池供應原料的日本住友金屬礦山(Sumitomo Metal Mining)也宣布將擴大產能。
特斯拉轉盈
特斯拉今年第三季的淨營收為2,190萬美元,EPS每股0.14美元,較去年每股虧損1.78美元有大幅成長。經會計調整後,第三季EPS為0.71美元,遠高於原先路透社調查預估的虧損0.54美元。會計調整後營收23億美元,也優於原先法人預期的19.8億美元。
特斯拉執行長Elon Musk在財報說明會上表示,今年下半年仍力求出貨5萬輛電動車的目標,並將致力於提升毛利率;在此基礎下,Musk看好第四季營收仍將持續獲利。
特斯拉亦指出,Model 3的交車時間表仍在軌道上,相當於駁斥Model 3將會面臨交車延遲的市場傳聞。特斯拉第三季財報的表現,也將影響股東於11月17日的股東會上針對是否併購SolarCity之投票案的意願。
特斯拉訂單多,上游樂
特斯拉的車用電池由Panasonic提供,而Panasonic的鋰離子電池其中一項原料──鎳酸鋰,則幾乎都由日本住友金屬礦山供應。因應特斯拉訂單強勁,以及全球電動車需求成長,住友金屬礦山宣布砸下180億日圓資金,擴大鎳酸鋰產能。
住友金屬礦山目前的鎳酸鋰每月產能約為1,850噸,預計在2018年1月擴產至3,550噸,以供應未來電動車用鋰電池所需。同時,住友金屬礦山也與Panasonic合作研發高性能鎳酸鋰的產能。
住友金屬礦山的擴產計畫,與特斯拉預計在2017年開始出貨Model 3有直接關聯。加上特斯拉預計2018年要出貨50萬輛Model 3,住友金屬礦山也因此將原先擴產到2,550噸的計畫提高到3,550噸。
其他鋰電池材料,如正負極、分隔膜、電解液等,日系、韓系廠商如住友化學、Toray、旭化成、昭和電工等的市占率相當高。這些廠商也都看好電動車市場的需求繼續成長,持續有擴產計畫,並與鋰電池大廠Panasonic、LG Chem等公司密切合作。
(照片來源: shared by CC 2.0)
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※為什麼 USB CONNECTOR 是電子產業重要的元件?
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※台北網頁設計公司全省服務真心推薦
※想知道最厲害的網頁設計公司“嚨底家”!
※推薦評價好的iphone維修中心
※聚甘新
目錄
我們知道在JVM中為了加快編譯速度,引入了JIT即時編譯的功能。那麼JIT什麼時候開始編譯的,又是怎麼編譯的,作為一個高傲的程序員,有沒有辦法去探究JIT編譯的秘密呢?答案是有的,今天和小師妹一起帶大家來看一看這個編譯背後的秘密。
更多精彩內容且看:
小師妹:F師兄,JIT這麼神器,但是好像就是一個黑盒子,有沒有辦法可以探尋到其內部的本質呢?
追求真理和探索精神是我們作為程序員的最大優點,想想如果沒有玻爾關於原子結構的新理論,怎麼會有原子體系的突破,如果沒有海森堡的矩陣力學,怎麼會有量子力學的建立?
JIT的編譯日誌輸出很簡單,使用 -XX:+LogCompilation就夠了。
如果要把日誌重定向到一個日誌文件中,則可以使用-XX:LogFile= 。
但是要開啟這些分析的功能,又需要使用-XX:+UnlockDiagnosticVMOptions。 所以總結一下,我們需要這樣使用:
-XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation -XX:LogFile=www.flydean.com.log
根據上面的介紹,我們現場來生成一個JIT的編譯日誌,為了體現出專業性,這裏我們需要使用到JMH來做性能測試。
JMH的全稱是Java Microbenchmark Harness,是一個open JDK中用來做性能測試的套件。該套件已經被包含在了JDK 12中。
如果你使用的不是JDK 12,那麼需要添加如下依賴:
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.19</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.19</version>
</dependency>
更多詳情可以參考我之前寫的: 在java中使用JMH(Java Microbenchmark Harness)做性能測試一文。
之前有的朋友說,代碼也用圖片,看起來好看,從本文之後,我們會盡量把代碼也轉成圖片來展示:
看完我的JMH的介紹,上面的例子應該很清楚了,主要就是做一個累加操作,然後warmup 5輪,測試5輪。
在@Fork註解裏面,我們可以配置jvm的參數,為什麼我註釋掉了呢?因為我發現在jvmArgsPrepend中的-XX:LogFile是不生效的。
沒辦法,我只好在運行配置中添加:
運行之後,你就可以得到輸出的編譯日誌文件。
小師妹:F師兄,我看了一下生成的文件好複雜啊,用肉眼能看得明白嗎?
別怕,只是內容的多一點,如果我們細細再細細的分析一下,你會發現其實它真的非常非常……複雜!
其實寫點簡單的小白文不好嗎?為什麼要來分析這麼複雜,又沒人看,看了也沒人懂的JVM底層…..
大概,這就是專業吧!
LogCompilation文件其實是xml格式的,我們現在來大概分析一下,它的結構,讓大家下次看到這個文件也能夠大概了解它的重點。
首先最基本的信息就是JVM的信息,包括JVM的版本,JVM運行的參數,還有一些properties屬性。
我們收集到的日誌其實是分兩類的,第一類是應用程序本身的的編譯日誌,第二類就是編譯線程自己內部產生的日誌。
第二類的日誌會以hs_c*.log的格式存儲,然後在JVM退出的時候,再將這些文件跟最終的日誌輸出文件合併,生成一個整體的日誌文件。
比如下面的兩個就是編譯線程內部的日誌:
<thread_logfile thread='22275' filename='/var/folders/n5/217y_bgn49z18zvjch907xb00000gp/T//hs_c22275_pid83940.log'/>
<thread_logfile thread='41731' filename='/var/folders/n5/217y_bgn49z18zvjch907xb00000gp/T//hs_c41731_pid83940.log'/>
上面列出了編譯線程的id=22275,如果我們順着22275找下去,則可以找到具體編譯線程的日誌:
<compilation_log thread='22275'>
...
</compilation_log>
上面由compilation_log圍起來的部分就是編譯日誌了。
接下來的部分表示,編譯線程開始執行了,其中stamp表示的是啟動時間,下圖列出了一個完整的編譯線程的日誌:
<start_compile_thread name='C2 CompilerThread0' thread='22275' process='83940' stamp='0.058'/>
接下來描述的是要編譯的方法信息:
<task compile_id='10' method='java.lang.Object <init> ()V' bytes='1' count='1409' iicount='1409' stamp='0.153'>
上面列出了要編譯的方法名,compile_id表示的是系統內部分配的編譯id,bytes是方法中的字節數,count表示的是該方法的調用次數,注意,這裏的次數並不是方法的真實調用次數,只能做一個估計。
iicount是解釋器被調用的次數。
task執行了,自然就會執行完成,執行完成的內容是以task_done標籤來表示的:
<task_done success='1' nmsize='120' count='1468' stamp='0.155'/>
其中success表示是否成功執行,nmsize表示編譯器編譯出來的指令大小,以byte為單位。如果有內聯的話,還有個inlined_bytes屬性,表示inlined的字節個數。
<type id='1025' name='void'/>
type表示的是方法的返回類型。
<klass id='1030' name='java.lang.Object' flags='1'/>
klass表示的是實例和數組類型。
<method id='1148' holder='1030' name='<init>' return='1025' flags='1' bytes='1' compile_id='1' compiler='c1' level='3' iicount='1419'/>
method表示執行的方法,holder是前面的klass的id,表示的是定義該方法的實例或者數組對象。method有名字,有
return,return對應的是上面的type。
flags表示的是方法的訪問權限。
接下來是parse,是分析階段的日誌:
<parse method='1148' uses='1419.000000' stamp='0.153'>
上面有parse的方法id。uses是使用次數。
<bc code='177' bci='0'/>
bc是byte Count的縮寫,code是byte的個數,bci是byte code的索引。
<dependency type='no_finalizable_subclasses' ctxk='1030'/>
dependency分析的是類的依賴關係,type表示的是什麼類型的依賴,ctkx是依賴的context class。
我們注意有的parse中,可能會有uncommon_trap:
<uncommon_trap bci='10' reason='unstable_if' action='reinterpret' debug_id='0' comment='taken never'/>
怎麼理解uncommon_trap呢?字面上意思就是捕獲非常用的代碼,就是說在解析代碼的過程中發現發現這些代碼是uncommon的,然後解析產生一個uncommon_trap,不再繼續進行了。
它裏面有兩個比較重要的字段,reason表示的是被標記為uncommon_trap的原因。action表示的出發uncommon_trap的事件。
有些地方還會有call:
<call method='1150' count='5154' prof_factor='1.000000' inline='1'/>
call的意思是,在該代碼中將會調用其他的方法。count是執行次數。
複雜的編譯日誌終於講完了,可能講的並不是很全,還有一些其他情況這裏並沒有列出來,後面如果遇到了,我再添加進去。
本文的例子https://github.com/ddean2009/learn-java-base-9-to-20
本文作者:flydean程序那些事
本文鏈接:http://www.flydean.com/jvm-jit-logcompilation/
本文來源:flydean的博客
歡迎關注我的公眾號:程序那些事,更多精彩等着您!
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※為什麼 USB CONNECTOR 是電子產業重要的元件?
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※台北網頁設計公司全省服務真心推薦
※想知道最厲害的網頁設計公司“嚨底家”!
※推薦評價好的iphone維修中心
※聚甘新
一.ConcurrentHashMap的模型圖
二.源碼分析-類定義
2.1 極簡ConcurrentHashMap定義
2.2 Segment內部類
2.3 HashEntry內部類
2.4 ConcurrentHashMap的重要常量
三.常用接口源碼分析
3.1 ConcurrentHashMap構造方法
3.2 map.put操作
3.3 創建新segment
3.4 segment.put操作
3.5 segment.rehash擴容
3.6 map.get操作
3.7 map.remove操作
3.8 map.size操作
原文地址:https://www.cnblogs.com/-beyond/p/13157083.html
之前看了Java8中的HashMap,然後想接着看Java8的ConcurrentHashMap,但是打開Java8的ConcurrentHashMap,瞬間就慫了,將近7k行代碼,而反觀一下Java7的Concurrent,也就在1500多行,那就先選擇少的看吧。畢竟Java7的ConcurrentHashMap更加簡單。下面所有的介紹都是針對Java7而言!!!!!
下面是ConcurrentHashMap的結構圖,ConcurrentHashMap有一個segments數組,每個segment中又有一個table數組,該數組的每個元素時HashEntry類型。
可以簡單的理解為ConcurrentHashMap是多個HashMap組成,每一個HashMap是一個segment,就如傳說中一樣,ConcurrentHashMap使用分段鎖的方式,這個“段”就是segment。
ConcurrentHashMap之所以能夠保證併發安全,是因為支持對不同segment的併發修改操作,比如兩個線程同時修改ConcurrentHashMap,一個線程修改第一個segment的數據,另一個線程修改第二個segment的數據,兩個線程可以併發修改,不會出現併發問題;但是多個線程同一個segment進行併發修改,則需要先獲取該segment的鎖后再修改,修改完后釋放鎖,然後其他要修改的線程再進行修改。
那麼,ConcurrentHashMap可以支持多少併發量呢?這個也就是問,ConcurrentHashMap最多能支持多少線程併發修改,其實也就是segment的數量,只要修改segment的這些線程不是修改同一個segment,那麼這些線程就可以并行執行,這也就是ConcurrentHashMap的併發量(segment的數量)。
注意,ConcurrentHashMap創建完成后,也就是segment的數量、併發級別已經確定,則segment的數量和併發級別都不能再改變了,即使發生擴容,也是segment中的table進行擴容,segment的數量保持不變。
下面是省略了大部分代碼的ConcurrentHashMap定義:
package java.util.concurrent;
import java.util.AbstractMap;
import java.util.concurrent.locks.ReentrantLock;
public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>, Serializable {
final Segment<K, V>[] segments;
/**
* segment分段的定義
*/
static final class Segment<K, V> extends ReentrantLock implements Serializable {
transient volatile HashEntry<K, V>[] table;
}
/**
* 存放的元素節點
*/
static final class HashEntry<K, V> {
}
}
ConcurrentHashMap內部有一個segments屬性,是Segment類型的數組,Segment類和HashMap很相似(Java7的HashMap),維持一個數組,每個數組是一個HashEntry,當發生hash衝突后,則使用拉鏈法(頭插法)來解決衝突。
而Segment數組的定義如下,省略了方法:
static final class Segment<K, V> extends ReentrantLock implements Serializable {
static final int MAX_SCAN_RETRIES = Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1;
private static final long serialVersionUID = 2249069246763182397L;
// segment的負載因子(segments數組中的所有segment負載因子都相同)
final float loadFactor;
// segment中保存元素的數組
transient volatile HashEntry<K, V>[] table;
// 該segment中的元素個數
transient int count;
// modify count,該segment被修改的次數
transient int modCount;
// segment的擴容閾值
transient int threshold;
}
注意每個Segment都有負載因子、以及擴容閾值,但是後面可以看到,其實segments數組中的每一個segment,負載因子和擴容閾值都相同,因為創建的時候,都是使用0號segment的負載因子和擴容閾值。
Segment類中有一個table數組,table數組的每個元素都是HashEntry類型,和HashMap的Entry類似,源碼如下:(省略了方法)
/**
* map中每個元素的類型
*/
static final class HashEntry<K, V> {
final int hash;
final K key;
volatile V value;
volatile HashEntry<K, V> next;
}
ConcurrentHashMap中有很多常量,
// 默認初始容量 static final int DEFAULT_INITIAL_CAPACITY = 16; // 默認的負載因子 static final float DEFAULT_LOAD_FACTOR = 0.75f; // 默認的併發級別(同時支持多少線程併發修改) // 因為JDK7中ConcurrentHashMap中是用分段鎖實現併發,不同分段的數據可以進行併發操作,同一個段的數據不能同時修改 static final int DEFAULT_CONCURRENCY_LEVEL = 16; // 最大容量 static final int MAXIMUM_CAPACITY = 1 << 30; // 每一個分段的數組容量初始值 static final int MIN_SEGMENT_TABLE_CAPACITY = 2; // 最多能有多少個segment static final int MAX_SEGMENTS = 1 << 16; // slightly conservative // 嘗試對整個map進行操作(比如說統計map的元素數量),可能需要鎖定全部segment; // 這個常量表示鎖定所有segment前,嘗試的次數 static final int RETRIES_BEFORE_LOCK = 2;
ConcurrentHashMap有多個構造方法,但是內部其實都是調用同一個進行創建:
public ConcurrentHashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
}
public ConcurrentHashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
}
public ConcurrentHashMap(int initialCapacity, float loadFactor) {
this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL);
}
/**
* 統一調用的構造方法
*
* @param initialCapacity 初始容量
* @param loadFactor 負載因子
* @param concurrencyLevel 併發級別
*/
@SuppressWarnings("unchecked")
public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) {
// 校驗參數合法性
if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0) {
throw new IllegalArgumentException();
}
// 對併發級別進行調整,不允許超過segment的數量(超過segment其實是沒有意義的)
if (concurrencyLevel > MAX_SEGMENTS) {
concurrencyLevel = MAX_SEGMENTS;
}
// 根據concurrencyLevel確定sshift和ssize的值
int ssize = 1; // ssize是表示segment的數量,ssize是不小於concurrencyLevel的數,並且是2的n次方
int sshift = 0;// sshift是ssize轉換為2進制后的位數,比如ssize為16(1000),則sshift為4
while (ssize < concurrencyLevel) {
++sshift;
ssize <<= 1;
}
// 比如concurrencyLevel默認為16,走完循環,sshift為4,ssize為16
// 如果concurrentLevel為8,則sshift為3,ssize為8
// segment的shift偏移量
this.segmentShift = 32 - sshift;
// segment掩碼
this.segmentMask = ssize - 1;
// 對傳入的初始容量進行調整(不允許大於最大容量)
if (initialCapacity > MAXIMUM_CAPACITY) {
initialCapacity = MAXIMUM_CAPACITY;
}
// 假設傳入的容量為128,併發級別為16,則initialCapacity為128,ssize為16
int c = initialCapacity / ssize;
// c可以理解為根據傳入的初始容量,計算出每個segment中的數組容量
if (c * ssize < initialCapacity) {
++c;
}
// cap表示的是每個segment中的數組容量
int cap = MIN_SEGMENT_TABLE_CAPACITY;
// 如果默認的每個segment中的數組長度小於上面計算出來的每個segment的數組長度,則將容量翻倍
while (cap < c) {
cap <<= 1;
}
// 創建一個segment,作為segments數組的第一個segment
Segment<K, V> s0 = new Segment<K, V>(loadFactor, (int) (cap * loadFactor), new HashEntry[cap]);
// 創建segments數組
Segment<K, V>[] ss = (Segment<K, V>[]) new Segment[ssize];
// 將s0賦值給segments數組的第一個元素(偏移量為0)
UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]
// 複製segment數組
this.segments = ss;
}
/**
* 傳入map,將map中的元素加入到ConcurrentHashMap中
* 注意使用默認的負載因子(0.75)和默認的併發級別(16)
* 初始容量取map容量和默認容量的較大值
*/
public ConcurrentHashMap(Map<? extends K, ? extends V> m) {
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY),
DEFAULT_LOAD_FACTOR,
DEFAULT_CONCURRENCY_LEVEL);
putAll(m);
}
map.put,map就是指ConcurrentHashMap,其實就是確定HashEntry應該放入哪個segment中的哪個位置,所以可分為3步:
1.首先需要確定放入哪個segment;
2.確定segment后,再確定HashEntry應該放入segment的哪個位置;
3.進行覆蓋覆蓋或者插入。
/**
* put操作,注意key或者value為null時,會拋出NPE
*/
@SuppressWarnings("unchecked")
public V put(K key, V value) {
Segment<K, V> s;
if (value == null) {
throw new NullPointerException();
}
// 計算key的hash
int hash = hash(key);
// hash值右移shift位后 與 mask掩碼進行取與,確定數據應該放到哪個segments數組的哪一個segment中
int j = (hash >>> segmentShift) & segmentMask;
// 判斷計算出的segment數組位置上的segment是否為null,如果為null,則進行創建segment
if ((s = (Segment<K, V>) UNSAFE.getObject(segments, (j << SSHIFT) + SBASE)) == null) {
s = ensureSegment(j);
}
// 創建segment后,將數據put到segment中,調用的segment.put()
return s.put(key, hash, value, false);
}
上面put的時候,如果發現segment為null,則會進行調用ensureSegment進行創建segment,代碼如下:
/**
* 擴容(創建)segment
*
* @param k 需要擴容或者需要創建的segment位置
* @return 返回擴容后的segment
*/
@SuppressWarnings("unchecked")
private Segment<K, V> ensureSegment(int k) {
final Segment<K, V>[] ss = this.segments;
long u = (k << SSHIFT) + SBASE; // 傳入的index,對應的偏移量
Segment<K, V> seg;
// 判斷是否需要擴容或者創建segment,如果獲取到segment不為null,則返回segment
if ((seg = (Segment<K, V>) UNSAFE.getObjectVolatile(ss, u)) == null) {
Segment<K, V> proto = ss[0]; // “原型設計模式”,使用segments數組的0號位置segment
int cap = proto.table.length;// 0號segment的table長度
float lf = proto.loadFactor; // 0號segment的負載因子
int threshold = (int) (cap * lf); // 0號segment的擴容閾值
// 創建一個HashEntry的數組,數組容量和0號segment相同
HashEntry<K, V>[] tab = (HashEntry<K, V>[]) new HashEntry[cap];
// 再次檢測,指定的segment是不是為null,如果為null才進行下一步操作
if ((seg = (Segment<K, V>) UNSAFE.getObjectVolatile(ss, u)) == null) { // recheck
// 創建segment
Segment<K, V> s = new Segment<K, V>(lf, threshold, tab);
// 使用CAS將新創建的segment填入指定的位置
while ((seg = (Segment<K, V>) UNSAFE.getObjectVolatile(ss, u)) == null) {
if (UNSAFE.compareAndSwapObject(ss, u, null, seg = s)) {
break;
}
}
}
}
// 返回新增的segment
return seg;
}
上面需要注意的是:
1.創建segment中的table數組時,是使用0號segment的table屬性(長度、負載因子、閾值);
2.創建segment前會進行再check,check發現segment的確為null時,再進行創建segment;
3.利用CAS來將創建的segment填入segments數組中;
當確定HashEntry應該放到哪個segment后,就開始調用segment的put方法,如下:
/**
* 在確定應該存放到那個segment后,就segment.put()將k-v存入segment中
*
* @param key put的key
* @param hash hash(key)的值
* @param value put的value
* @param onlyIfAbsent true:key對應的Entry不進行覆蓋,false:key對應的entry存在與否,都進行put覆蓋
* @return
*/
final V put(K key, int hash, V value, boolean onlyIfAbsent) {
// 先獲取鎖(ReentrantLock,內部使用非公平鎖)
HashEntry<K, V> node = tryLock() ? null : scanAndLockForPut(key, hash, value);
V oldValue;
try {
HashEntry<K, V>[] tab = table;
// 根據hash值計算出在segment的table中的位置
int index = (tab.length - 1) & hash;
// 定位到segment的table的index位置(第一個節點)
HashEntry<K, V> first = entryAt(tab, index);
// 從第一個節點開始遍歷
for (HashEntry<K, V> e = first; ; ) {
// 節點不為空,則判斷是否key是否相同(相同HashEntry)
if (e != null) {
K k;
// 比較是否key是否相等(判斷put的key是否已經存在)
if ((k = e.key) == key || (e.hash == hash && key.equals(k))) {
// 如果key已經存在,則進行覆蓋,如果onlyIsAbsent為false(不關心key對應的Entry是否存在)
oldValue = e.value;
if (!onlyIfAbsent) {
// 覆蓋舊值,修改計數加1
e.value = value;
++modCount;
}
break;
}
e = e.next;
} else {
// 頭插法,將put的k-v創建新HashEntry,放到first的前面
if (node != null) {
node.setNext(first);
} else {
node = new HashEntry<K, V>(hash, key, value, first);
}
// segment中table元素數量加1
int c = count + 1;
// 加1后的size大於擴容閾值,且數組的長度小於最大容量,則進行rehash
if (c > threshold && tab.length < MAXIMUM_CAPACITY) {
// 擴容,並進行rehash
rehash(node);
} else {
// 將節點放入數組中的指定位置
setEntryAt(tab, index, node);
}
// 修改次數加一,修改segment的table元素個數
++modCount;
count = c;
// 舊值為null
oldValue = null;
break;
}
}
} finally {
// 釋放鎖
unlock();
}
return oldValue;
}
梳理一下,大致在做下面幾件事:
1.先獲取鎖(ReetrantLock,內部使用非公平鎖NonFairSync);
2.獲取到鎖后根據hash計算出在table的位置;
3.遍歷table的HashEntry的鏈表,如果有相同key的,則進行覆蓋,如果沒有,在進行頭插法;
4.插入鏈表后,確定是否需要擴容,需要則執行rehash;
5.修改計數(count、modCount),並且釋放鎖。
segment擴容時,會將該segment的容量擴容為之前的2倍,並且將各位置的鏈表節點元素進行rehash。
/**
* 將segment的table容量擴容一倍(newCap=oldCap*2),注意只會擴容該node所在的segment
*
* @param node segment[i]->鏈表的頭結點
*/
@SuppressWarnings("unchecked")
private void rehash(HashEntry<K, V> node) {
HashEntry<K, V>[] oldTable = table;
int oldCapacity = oldTable.length;
// 新容量為舊容量的2倍
int newCapacity = oldCapacity << 1;
// 設置新的擴容閾值
threshold = (int) (newCapacity * loadFactor);
// 申請新數組,數組長度為新容量值
HashEntry<K, V>[] newTable = (HashEntry<K, V>[]) new HashEntry[newCapacity];
// 循環遍歷segment的數組(舊數組)
int sizeMask = newCapacity - 1; // 新的掩碼
for (int i = 0; i < oldCapacity; i++) {
// 獲取第i個位置的HashEntry節點,如果該節點為null,則該位置為空,不作處理
HashEntry<K, V> e = oldTable[i];
if (e != null) {
HashEntry<K, V> next = e.next;
// 計算新位置
int idx = e.hash & sizeMask;
// next為null,表示該位置只有一個節點,直接將節點複製到新位置
if (next == null) { // Single node on list
newTable[idx] = e;
} else { // Reuse consecutive sequence at same slot
HashEntry<K, V> lastRun = e;
int lastIdx = idx;
for (HashEntry<K, V> last = next; last != null; last = last.next) {
int k = last.hash & sizeMask;
if (k != lastIdx) {
lastIdx = k;
lastRun = last;
}
}
newTable[lastIdx] = lastRun;
// 從頭結點開始,開始將節點拷貝到新數組中
for (HashEntry<K, V> p = e; p != lastRun; p = p.next) {
V v = p.value;
int h = p.hash;
int k = h & sizeMask;
HashEntry<K, V> n = newTable[k];
newTable[k] = new HashEntry<K, V>(h, p.key, v, n);
}
}
}
}
// 將頭結點添加到segment的table中
int nodeIndex = node.hash & sizeMask; // add the new node
node.setNext(newTable[nodeIndex]);
newTable[nodeIndex] = node;
table = newTable;
}
為啥擴容的時候沒有加鎖呀?
其實已經加鎖了,只不過不是在rehash中加鎖!!!因為rehash是在map.put中調用,put的時候已經加鎖了,所以rehash中不用加鎖。
get操作,先定位到segment,然後定位到segment的具體位置,進行獲取
/**
* 從ConcurrentHashMap中獲取key對應的value,不需要加鎖
*/
public V get(Object key) {
Segment<K, V> s;
HashEntry<K, V>[] tab;
// 計算key的hash
int h = hash(key);
// 計算key處於哪一個segment中(index)
long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
// 獲取數組中該位置的segment,如果該segment的table不為空,那麼就繼續在segment中查找,否則返回null,因為未找到
if ((s = (Segment<K, V>) UNSAFE.getObjectVolatile(segments, u)) != null && (tab = s.table) != null) {
// tab指向segment的table數組,通過hash計算定位到table數組的位置(然後開始遍歷鏈表)
HashEntry<K, V> e;
for (e = (HashEntry<K, V>) UNSAFE.getObjectVolatile(tab, ((long) (((tab.length - 1) & h)) << TSHIFT) + TBASE);
e != null; e = e.next) {
K k;
// 判斷key和hash是否匹配,匹配則證明找到要查找的數據,將數據返回
if ((k = e.key) == key || (e.hash == h && key.equals(k)))
return e.value;
}
}
return null;
}
刪除節點,和get的流程差不多,先定位到segment,然後確定segment的哪個位置(哪條鏈表),遍歷鏈表,找到後進行刪除。
/**
* 刪除map中key對應的元素
*/
public V remove(Object key) {
// 計算key的hash
int hash = hash(key);
// 根據hash確定segment
Segment<K, V> s = segmentForHash(hash);
// 調用segment.remove進行刪除
return s == null ? null : s.remove(key, hash, null);
}
/**
* 刪除segment中key對應的hashEntry
* 如果傳入的value不為空,則會在value匹配的時候進行刪除,否則不操作
*/
final V segmeng.remove(Object key, int hash, Object value) {
// 獲取鎖失敗,則不斷自旋嘗試獲取鎖
if (!tryLock()) {
scanAndLock(key, hash);
}
V oldValue = null;
try {
HashEntry<K, V>[] tab = table;
// 定位到segment中table的哪個位置
int index = (tab.length - 1) & hash;
HashEntry<K, V> e = entryAt(tab, index);
HashEntry<K, V> pred = null;
// 遍歷鏈表
while (e != null) {
K k;
HashEntry<K, V> next = e.next;
// 如果key和hash都匹配
if ((k = e.key) == key || (e.hash == hash && key.equals(k))) {
V v = e.value;
// 如果沒有傳入value,則直接刪除該節點
// 如果傳入了value,比如調用的map.remove(key,value),則要value匹配才會刪除,否則不操作
if (value == null || value == v || value.equals(v)) {
if (pred == null) { // 頭結點就是要找刪除的元素,next為null,則將null賦值數組的該位置
setEntryAt(tab, index, next);
} else { //
pred.setNext(next);
}
// 修改次數加一,map數量減一
++modCount;
--count;
oldValue = v;
}
break;
}
// 不匹配時,pred保存當前一次檢測的節點,e指向下一個節點
pred = e;
e = next;
}
} finally {
unlock();// 釋放鎖
}
return oldValue;
}
ConcurrentHashMap的size(),需要統計每一個segment中的元素數量(也就是count值),因為不同的segment允許併發修改,統計過程中可能會出現修改操作,導致統計不準確,所以要想準確統計整個map的元素數量,可以這樣做:通過加鎖的方式來解決(將所有的segment都加鎖),這樣就能保證元素不會變化了,這是我們的想法。
而ConcurrentHashMap是這樣解決的,先嘗試不加鎖進行統計兩次,這兩次統計,不止會統計每個segment的元素數量,還會統計每個segment的modCount(修改次數),進行匯總;如果兩次統計的modCount總量相同,也就說明兩次統計期間沒有修改操作,證明統計的元素總量正確;如果兩次modCount總量不相同,表示有修改操作,則進行重試,如果重試后,發現還是不準確(modCount不匹配),那麼就嘗試為所有的segment加鎖,再進行統計。
源碼如下:
/**
* 獲取ConcurrentHashMap中的元素個數,如果元素個數超過Integer.MAX_VALUE,則返回Integer.MAX_VALUE
*/
public int size() {
final Segment<K, V>[] segments = this.segments;
int size; // 返回元素數量(統計結果->元素的總量)
boolean overflow; // 標誌是否溢出(是否超過Integer.MAX_VALUE)
long sum; // 所有segment的modCount總量次數(整個map的修改次數)
long last = 0L; // previous sum,上一輪統計的modCount總量
int retries = -1; // 記錄重試的次數
try {
// 此處for循環主要用於控制重試
for (; ; ) {
// 重試的次數如果達到RETRIES_BEFORE_LOCK,則強制獲取所有segment的鎖
if (retries++ == RETRIES_BEFORE_LOCK) {
for (int j = 0; j < segments.length; ++j) {
ensureSegment(j).lock();
// 強制獲取segment的table第i個位置,並加鎖
}
}
sum = 0L;
size = 0;
overflow = false;
// 開始對segments中的每一個segment中進行統計
for (int j = 0; j < segments.length; ++j) {
// 獲取第j個segment
Segment<K, V> seg = segmentAt(segments, j);
// 如果segment不為空,則進行統計
if (seg != null) {
sum += seg.modCount;
int c = seg.count;
// size累加
if (c < 0 || (size += c) < 0)
overflow = true;
}
}
// 如果本次統計的modCount總量和上次一樣,則表示在這兩次統計之間沒有進行過修改,則不再重試
if (sum == last) {
break;
}
// 記錄本次統計的modCount總量
last = sum;
}
} finally {
// 判斷是否加了鎖(如果retries大於RETRIES_BEFORE_LOCK),則證明加了鎖,於是進行釋放鎖
if (retries > RETRIES_BEFORE_LOCK) {
for (int j = 0; j < segments.length; ++j)
segmentAt(segments, j).unlock();
}
}
return overflow ? Integer.MAX_VALUE : size;
}
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※為什麼 USB CONNECTOR 是電子產業重要的元件?
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※台北網頁設計公司全省服務真心推薦
※想知道最厲害的網頁設計公司“嚨底家”!
※推薦評價好的iphone維修中心
※聚甘新