新博課主要用于記錄一些系列的技術(shù)文章,今年的一個目標(biāo)就是深入研究JVM底層,我會把一些學(xué)習(xí)心得記錄下來,也歡迎大家一起討論~
posted @ 2020-04-18 10:12 楊羅羅 閱讀(222) | 評論 (0) | 編輯 收藏
博客做了遷移,新的地址:觀云的BLOG https://yeas.fun/
新博課主要用于記錄一些系列的技術(shù)文章,今年的一個目標(biāo)就是深入研究JVM底層,我會把一些學(xué)習(xí)心得記錄下來,也歡迎大家一起討論~ posted @ 2020-04-18 10:12 楊羅羅 閱讀(222) | 評論 (0) | 編輯 收藏 現(xiàn)在網(wǎng)絡(luò)那么發(fā)達(dá),我們上網(wǎng)的每個人勢必會在各個網(wǎng)站上登陸,那勢必會有一堆密碼需要管理,那怎么能記住那么多網(wǎng)站的密碼呢?我之前的做法是設(shè)置幾個常用的密碼,好多不重要的網(wǎng)站用一個,重要的網(wǎng)站用一個,然后...CSDN密碼泄露之后,只得吭哧吭哧的改一堆密碼。那種痛苦真的是呵呵呵 那有沒有什么方式可以方便的管理密碼呢?那就是LastPass的工作,它是一款跨平臺密碼管理軟件。你在每個網(wǎng)站上都可以隨機(jī)生成一個密碼,然后軟件會自動記住,你只需要記住這個軟件的主密碼就可以了。超方便! 為什么要用LastPass?我用LastPass,是因為它可以安裝瀏覽器插件,之后你在網(wǎng)站的注冊,它會自動提醒你要不要加入它的密碼庫,你在網(wǎng)站的登陸它會自動幫你填寫賬號密碼,甚至于一些常用的表單(比如說身份證、公司地址、銀行卡等)你可以提前設(shè)置好,它也會自動幫你填寫,我再也不用記那么多密碼了,一切都是自動化。 如果你經(jīng)常有國外的帳號,比如說google等大公司,LastPass甚至提供了一鍵改密碼的功能,好方便! 支持智能手機(jī)么?當(dāng)然!現(xiàn)在智能機(jī)那么流行,不能在智能機(jī)上用,簡直不能忍。當(dāng)你安裝了IOS軟件之后,在打開網(wǎng)站登錄界面,點擊下方的向上小箭頭,選擇LassPass軟件,點擊Touch ID,好了,密碼自動完成,世界頓時清凈了,想想以前在手機(jī)上輸入超長的密碼,跪了! 真的安全嗎?有的人擔(dān)心密碼泄露問題,其實對于LastPass沒啥必要,因為LastPass存儲的都是加密文件,只要你的主密碼不泄露,別人即使拿到你的網(wǎng)上的密碼,也是加密的,沒法用。 它收費嗎?好東西都要收費,價格也還可以,幾十塊一年,其實收費版和免費版對于普通用戶,最重要的區(qū)別就是:免費版帳號密碼不能云同步 免費獲得一個月的高級賬戶權(quán)限https://lastpass.com/f?18430702 通過這個地址注冊,則會免費獲得一個月高級賬戶權(quán)限 最后也是最重要的:點擊這個鏈接,輸入剛剛你注冊的郵箱,則會送半年的高級賬戶,記住一個密碼,就記住了所有密碼,就是那么簡單! posted @ 2016-01-13 14:19 楊羅羅 閱讀(353) | 評論 (0) | 編輯 收藏 一. 應(yīng)用場景 在大型分布式應(yīng)用中,我們經(jīng)常碰到在多數(shù)據(jù)庫之間的數(shù)據(jù)同步問題,比如說一款游戲,在玩家注冊后,可以馬上登陸進(jìn)入服務(wù)器,即數(shù)據(jù)在一個IDC更新,其它IDC立即可見。為了簡化思路,我們這里稱玩家注冊的數(shù)據(jù)庫(數(shù)據(jù)來源庫)為中心庫,同步目的地的數(shù)據(jù)庫為分站庫。 在分布式領(lǐng)域有個CAP理論,是說Consistency(一致性), Availability(可用性), Partition tolerance(分區(qū)和容錯) 三部分在系統(tǒng)實現(xiàn)只可同時滿足二點,無法三者兼顧。 能做的 · 數(shù)據(jù)快速搬運到指定的IDC節(jié)點 · 數(shù)據(jù)傳遞過程中失敗時,重新傳遞 · 監(jiān)控數(shù)據(jù)傳遞流程 · 故障轉(zhuǎn)移 · 數(shù)據(jù)版本控制 · 分配全局唯一的ID 不能做的 · 不參與業(yè)務(wù)行為,業(yè)務(wù)操作只能通過注冊的方式集成 · 不保存業(yè)務(wù)數(shù)據(jù),不提供傳遞的業(yè)務(wù)的查詢 二.系統(tǒng)要求 1.數(shù)據(jù)快速同步:除去網(wǎng)絡(luò)原因,正常情況下從來源庫同步到接收庫的時間不超過300m2.高并發(fā):單個應(yīng)用每秒同步2000條記錄 三.設(shè)計思路 系統(tǒng)優(yōu)化,最常用的就是進(jìn)行業(yè)務(wù)切割,將總?cè)蝿?wù)切割為許多子任務(wù),分區(qū)塊分析系統(tǒng)中可能存在的性能瓶頸并有針對性地進(jìn)行優(yōu)化,在本系統(tǒng)中,主要業(yè)務(wù)包含以下內(nèi)容: 四.數(shù)據(jù)流轉(zhuǎn) 綠色-正常流程、紅色-異常流程 隊列處理 根據(jù)業(yè)務(wù)劃分隊列名稱,每個隊列再劃分為三個關(guān)聯(lián)隊列:正常隊列(Normal)、重試隊列(Retry)、死亡隊列(Death),處理流程為: 1 【進(jìn)程A】把數(shù)據(jù)先放入正常隊列,如果放置失敗寫恢復(fù)日志 2 【進(jìn)程B】監(jiān)聽正常隊列,獲取隊列數(shù)據(jù)并進(jìn)行業(yè)務(wù)處理,處理失敗放入重試隊列 3 【進(jìn)程C】監(jiān)聽重試隊列,過幾秒獲取隊列數(shù)據(jù)并重新進(jìn)行業(yè)務(wù)處理,處理失敗放入死亡隊列 4 【進(jìn)程D】監(jiān)聽死亡隊列,獲取隊列數(shù)據(jù)并重新進(jìn)行業(yè)務(wù)處理,處理失敗重新放入死亡隊列尾部,等待下一次輪回 業(yè)務(wù)處理失敗如果無法再次放入隊列,記錄恢復(fù)日志 數(shù)據(jù)同步流程 1發(fā)送數(shù)據(jù),支持Http POST:curl -d "經(jīng)過URL編碼的文本消息",如"http://host:port/sync_all/register" 5 sync-syncer接收到同步數(shù)據(jù)請求,創(chuàng)建sid并分解出需要同步的節(jié)點個數(shù),把原始數(shù)據(jù)和子任務(wù)寫入redis中,sid寫入httpsqs中 6 sync-delivery監(jiān)聽中心httpsqs隊列,根據(jù)sid從redis獲取到原始數(shù)據(jù)和需要同步的節(jié)點地址,往其他節(jié)點發(fā)送數(shù)據(jù),流程如按"隊列處理流程"進(jìn)行 7 sync-batch監(jiān)聽分節(jié)點的httpsqs隊列,調(diào)用已經(jīng)注冊的處理器處理隊列數(shù)據(jù),流程如按"隊列處理流程"進(jìn)行 三. 恢復(fù)和監(jiān)控 恢復(fù)數(shù)據(jù)源 · httpsqs中的死亡隊列 - 業(yè)務(wù)處理暫時處理不了的數(shù)據(jù) · recovery日志文件 - 其它異常情況下的數(shù)據(jù),例如網(wǎng)絡(luò)無法連接、內(nèi)部服務(wù)不可用 數(shù)據(jù)恢復(fù) 獨立的應(yīng)用來處理正常流程中沒有完成的任務(wù),主要功能有: · 監(jiān)聽死亡隊列,進(jìn)行業(yè)務(wù)重做,再次執(zhí)行失敗時將執(zhí)行次數(shù)+1,最大執(zhí)行次數(shù)為5(默認(rèn)),超出上限則記錄到恢復(fù)日志中 · 讀取恢復(fù)日志,重新放入死亡隊列 應(yīng)用監(jiān)控 · 使用scribe日志框架服務(wù)業(yè)務(wù)日志的采集和監(jiān)控 · 收集重要的業(yè)務(wù)操作日志 · 動態(tài)的開啟/關(guān)閉某類業(yè)務(wù)日志 · 對redis進(jìn)行監(jiān)控 · 對httpsps,監(jiān)控隊列個數(shù),每個隊列的狀態(tài) 四. 數(shù)據(jù)結(jié)構(gòu) {"sid":111,"type":"reg","v":1,"data":"hello world","ctime":65711321800,"exec":1} · sid(sync id) - 全局唯一id · v(version) - 版本號 · data - 業(yè)務(wù)數(shù)據(jù) · ctime(create time) - 創(chuàng)建時間(毫秒) · exec - 可選,執(zhí)行次數(shù)
所有的key都小寫,以 ':' 作為分隔符 五.編碼及測試結(jié)果 經(jīng)過編碼和測試,在內(nèi)網(wǎng)環(huán)境下,在無數(shù)據(jù)庫限制的情況下,單應(yīng)用可以傳遞1500條/秒,基本滿足業(yè)務(wù)需求。如果需進(jìn)一步擴(kuò)展,采用集群式布署可使得吞吐量成倍的增長。 posted @ 2011-04-06 15:50 楊羅羅 閱讀(3639) | 評論 (3) | 編輯 收藏 摘要: java.util.concurrent 包含許多線程安全、測試良好、高性能的并發(fā)構(gòu)建塊。不客氣地說,創(chuàng)建 java.util.concurrent 的目的就是要實現(xiàn) Collection 框架對數(shù)據(jù)結(jié)構(gòu)所執(zhí)行的并發(fā)操作。通過提供一組可靠的、高性能并發(fā)構(gòu)建塊,開發(fā)人員可以提高并發(fā)類的線程安全、可伸縮性、性能、可讀性和可靠性。
如果一些類名看起來相似,可能是因為 java.util.concurr... 閱讀全文
posted @ 2010-12-08 17:40 楊羅羅 閱讀(820) | 評論 (0) | 編輯 收藏 AQS中有一個state字段(int類型,32位)用來描述有多少線程獲持有鎖。在獨占鎖的時代這個值通常是0或者1(如果是重入的就是重入的次數(shù)),在共享鎖的時代就是持有鎖的數(shù)量。
自旋等待適合于比較短的等待,而掛起線程比較適合那些比較耗時的等待。 鎖競爭 影響鎖競爭性的條件有兩個:鎖被請求的頻率和每次持有鎖的時間。顯然當(dāng)而這二者都很小的時候,鎖競爭不會成為主要的瓶頸。但是如果鎖使用不當(dāng),導(dǎo)致二者都比較大,那么很有可能CPU不能有效的處理任務(wù),任務(wù)被大量堆積。 所以減少鎖競爭的方式有下面三種:
死鎖 1.一種情況是線程A永遠(yuǎn)不釋放鎖,結(jié)果B一直拿不到鎖,所以線程B就“死掉”了 避免死鎖的解決方案是: posted @ 2010-12-03 10:11 楊羅羅 閱讀(1845) | 評論 (0) | 編輯 收藏 摘要: 內(nèi)部類詳解
1、定義
一個類的定義放在另一個類的內(nèi)部,這個類就叫做內(nèi)部類。
Java代碼
public class First {
public class Contents{
&nb... 閱讀全文
posted @ 2010-11-25 16:27 楊羅羅 閱讀(5038) | 評論 (1) | 編輯 收藏
Spring中提供一些Aware相關(guān)接口,像是BeanFactoryAware、 ApplicationContextAware、ResourceLoaderAware、ServletContextAware等等,實現(xiàn)這些 Aware接口的Bean在被初始之后,可以取得一些相對應(yīng)的資源,例如實現(xiàn)BeanFactoryAware的Bean在初始后,Spring容器將會注入BeanFactory的實例,而實現(xiàn)ApplicationContextAware的Bean,在Bean被初始后,將會被注入 ApplicationContext的實例等等。 Bean取得BeanFactory、ApplicationContextAware的實例目的是什么,一般的目的就是要取得一些檔案資源的存取、相 關(guān)訊息資源或是那些被注入的實例所提供的機(jī)制,例如ApplicationContextAware提供了publishEvent()方法,可以支持基于Observer模式的事件傳播機(jī)制。 ApplicationContextAware接口的定義如下: ApplicationContextAware.java public interface ApplicationContextAware { void setApplicationContext(ApplicationContext context); }
我們這邊示范如何透過實現(xiàn)ApplicationContextAware注入ApplicationContext來實現(xiàn)事件傳播,首先我們的HelloBean如下: HelloBean.java package onlyfun.caterpillar;
import org.springframework.context.*;
public class HelloBean implements ApplicationContextAware { private ApplicationContext applicationContext; private String helloWord = "Hello!World!";
public void setApplicationContext(ApplicationContext context) { this.applicationContext = context; }
public void setHelloWord(String helloWord) { this.helloWord = helloWord; }
public String getHelloWord() { applicationContext.publishEvent( new PropertyGettedEvent("[" + helloWord + "] is getted")); return helloWord; } }
ApplicationContext會由Spring容器注入,publishEvent()方法需要一個繼承ApplicationEvent的對象,我們的PropertyGettedEvent繼承了ApplicationEvent,如下: PropertyGettedEvent.java package onlyfun.caterpillar;
import org.springframework.context.*;
public class PropertyGettedEvent extends ApplicationEvent { public PropertyGettedEvent(Object source) { super(source); } }
當(dāng)ApplicationContext執(zhí)行publishEvent()后,會自動尋找實現(xiàn)ApplicationListener接口的對象并通知其發(fā)生對應(yīng)事件,我們實現(xiàn)了PropertyGettedListener如下: PrppertyGettedListener.java package onlyfun.caterpillar;
import org.springframework.context.*;
public class PropertyGettedListener implements ApplicationListener { public void onApplicationEvent(ApplicationEvent event) { System.out.println(event.getSource().toString()); } }
Listener必須被實例化,這我們可以在Bean定義檔中加以定義: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="propertyGetterListener" class="onlyfun.caterpillar.PropertyGettedListener"/>
<bean id="helloBean" class="onlyfun.caterpillar.HelloBean"> <property name="helloWord"><value>Hello!Justin!</value></property> </bean> </beans>
我們寫一個測試程序來測測事件傳播的運行: Test.java package onlyfun.caterpillar;
import org.springframework.context.*; import org.springframework.context.support.*;
public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
HelloBean hello = (HelloBean) context.getBean("helloBean"); System.out.println(hello.getHelloWord()); } }
執(zhí)行結(jié)果會如下所示: log4j:WARN No appenders could be found for logger (org.springframework.beans.factory.xml.XmlBeanDefinitionReader). log4j:WARN Please initialize the log4j system properly. org.springframework.context.support.ClassPathXmlApplicationContext: displayName=[org.springframework.context.support.ClassPathXmlApplicationContext; hashCode=33219526]; startup date=[Fri Oct 29 10:56:35 CST 2004]; root of ApplicationContext hierarchy [Hello!Justin!] is getted Hello!Justin!
以上是以實現(xiàn)事件傳播來看看實現(xiàn)Aware接口取得對應(yīng)對象后,可以進(jìn)行的動作,同樣的,您也可以實現(xiàn)ResourceLoaderAware接口: ResourceLoaderAware.java public interface ResourceLoaderAware { void setResourceLoader(ResourceLoader loader); }
實現(xiàn)ResourceLoader的Bean就可以取得ResourceLoader的實例,如此就可以使用它的getResource()方法,這對于必須存取檔案資源的Bean相當(dāng)有用。 基本上,Spring雖然提供了這些Aware相關(guān)接口,然而Bean上若實現(xiàn)了這些界面,就算是與Spring發(fā)生了依賴,從另一個角度來看,雖然您可以直接在Bean上實現(xiàn)這些接口,但您也可以透過setter來完成依賴注入,例如: HelloBean.java package onlyfun.caterpillar;
import org.springframework.context.*;
public class HelloBean { private ApplicationContext applicationContext; private String helloWord = "Hello!World!";
public void setApplicationContext(ApplicationContext context) { this.applicationContext = context; }
public void setHelloWord(String helloWord) { this.helloWord = helloWord; }
public String getHelloWord() { applicationContext.publishEvent(new PropertyGettedEvent("[" + helloWord + "] is getted")); return helloWord; } }
注意這次我們并沒有實現(xiàn)ApplicationContextAware,我們在程序中可以自行注入ApplicationContext實例: ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
HelloBean hello = (HelloBean) context.getBean("helloBean"); hello.setApplicationContext(context); System.out.println(hello.getHelloWord());
就Bean而言,降低了對Spring的依賴,可以比較容易從現(xiàn)有的框架中脫離。
posted @ 2010-11-24 11:14 楊羅羅 閱讀(7665) | 評論 (1) | 編輯 收藏 在JDK 5之前Java語言是靠synchronized關(guān)鍵字保證同步的,這會導(dǎo)致有鎖(后面的章節(jié)還會談到鎖)。 鎖機(jī)制存在以下問題: (1)在多線程競爭下,加鎖、釋放鎖會導(dǎo)致比較多的上下文切換和調(diào)度延時,引起性能問題。 (2)一個線程持有鎖會導(dǎo)致其它所有需要此鎖的線程掛起。 (3)如果一個優(yōu)先級高的線程等待一個優(yōu)先級低的線程釋放鎖會導(dǎo)致優(yōu)先級倒置,引起性能風(fēng)險。 volatile是不錯的機(jī)制,但是volatile不能保證原子性。因此對于同步最終還是要回到鎖機(jī)制上來。 獨占鎖是一種悲觀鎖,synchronized就是一種獨占鎖,會導(dǎo)致其它所有需要鎖的線程掛起,等待持有鎖的線程釋放鎖。而另一個更加有效的鎖就是樂觀鎖。所謂樂觀鎖就是,每次不加鎖而是假設(shè)沒有沖突而去完成某項操作,如果因為沖突失敗就重試,直到成功為止。 上面的樂觀鎖用到的機(jī)制就是CAS,Compare and Swap。 CAS有3個操作數(shù),內(nèi)存值V,舊的預(yù)期值A(chǔ),要修改的新值B。當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時,將內(nèi)存值V修改為B,否則什么都不做。 非阻塞算法 (nonblocking algorithms)
現(xiàn)代的CPU提供了特殊的指令,可以自動更新共享數(shù)據(jù),而且能夠檢測到其他線程的干擾,而 compareAndSet() 就用這些代替了鎖定。 拿出AtomicInteger來研究在沒有鎖的情況下是如何做到數(shù)據(jù)正確性的。
首先毫無以為,在沒有鎖的機(jī)制下可能需要借助volatile原語,保證線程間的數(shù)據(jù)是可見的(共享的)。這樣才獲取變量的值的時候才能直接讀取。
然后來看看++i是怎么做到的。
在這里采用了CAS操作,每次從內(nèi)存中讀取數(shù)據(jù)然后將此數(shù)據(jù)和+1后的結(jié)果進(jìn)行CAS操作,如果成功就返回結(jié)果,否則重試直到成功為止。 而compareAndSet利用JNI來完成CPU指令的操作。
整體的過程就是這樣子的,利用CPU的CAS指令,同時借助JNI來完成Java的非阻塞算法。其它原子操作都是利用類似的特性完成的。 而整個J.U.C都是建立在CAS之上的,因此對于synchronized阻塞算法,J.U.C在性能上有了很大的提升。 CAS算法實現(xiàn)一個重要前提需要取出內(nèi)存中某時刻的數(shù)據(jù),而在下時刻比較并替換,那么在這個時間差類會導(dǎo)致數(shù)據(jù)的變化。 比如說一個線程one從內(nèi)存位置V中取出A,這時候另一個線程two也從內(nèi)存中取出A,并且two進(jìn)行了一些操作變成了B,然后two又將V位置的數(shù)據(jù)變成A,這時候線程one進(jìn)行CAS操作發(fā)現(xiàn)內(nèi)存中仍然是A,然后one操作成功。盡管線程one的CAS操作成功,但是不代表這個過程就是沒有問題的。如果鏈表的頭在變化了兩次后恢復(fù)了原值,但是不代表鏈表就沒有變化。因此前面提到的原子操作AtomicStampedReference/AtomicMarkableReference就很有用了。這允許一對變化的元素進(jìn)行原子操作。 posted @ 2010-11-18 15:16 楊羅羅 閱讀(3130) | 評論 (1) | 編輯 收藏 volatile保證線程間的數(shù)據(jù)是可見的(共享的),但不保證數(shù)據(jù)同步 ![]() ![]() ![]() ![]() ![]() ![]() ![]() 應(yīng)用volatile變量的三個原則:
posted @ 2010-11-18 14:45 楊羅羅 閱讀(1714) | 評論 (0) | 編輯 收藏 摘要: JMM規(guī)范:
The rules for happens-before are:
Program order rule. Each action in a thread happens-before every action in that thread that comes later in the program order.
... 閱讀全文
posted @ 2010-11-18 14:42 楊羅羅 閱讀(1092) | 評論 (0) | 編輯 收藏
|
|||||||||||||||||||||