并发编程-08安全发布对象之发布与逸出
文章目錄
- 腦圖
- 概念
- 示例
- 不安全的發(fā)布對(duì)象Demo
- 對(duì)象逸出Demo
- 小結(jié)
- 代碼
腦圖
概念
發(fā)布對(duì)象: 使一個(gè)對(duì)象能夠被當(dāng)前范圍之外的代碼所使用,日常開發(fā)中比較常見的比如通過(guò)類的非私有方法返回對(duì)象的引用,或者通過(guò)公有的靜態(tài)變量發(fā)布對(duì)象 等都屬于發(fā)布對(duì)象
對(duì)象逸出: 首先需要明確的是對(duì)象逸出是一種錯(cuò)誤的發(fā)布方式。 當(dāng)一個(gè)對(duì)象還沒有構(gòu)造完成時(shí),就使它被其他線程所見。
示例
不安全的發(fā)布對(duì)象Demo
package com.artisan.example.publish;import com.artisan.anno.NotThreadSafe;import lombok.extern.slf4j.Slf4j;@Slf4j @NotThreadSafe public class UnSafePublishObjectDemo {// 私有變量private String name = "artisan";// 通過(guò)public訪問(wèn)級(jí)別的方法getName發(fā)布了類的域,在類的外部,任何線程都可以訪問(wèn)這個(gè)域// 這樣發(fā)布的對(duì)象是不安全的,因?yàn)槲覀儫o(wú)法得知其他線程是否會(huì)修改這個(gè)域?qū)е略擃惱飻?shù)據(jù)的錯(cuò)誤public String getName() {return name;}public static void main(String[] args) {// 通過(guò)new實(shí)例化UnSafePublishObjectDemoUnSafePublishObjectDemo unSafePublishObjectDemo = new UnSafePublishObjectDemo();// 調(diào)用getName()方法得到私有屬性的引用String name = unSafePublishObjectDemo.getName();log.info("name:{}",name);// 假設(shè)有第二個(gè)線程去修改name屬性的值String name2 = unSafePublishObjectDemo.getName();name2 = "小工匠";log.info("name:{}",name2);}}上面的代碼里,通過(guò)new對(duì)象初始化了UnSafePublishObjectDemo對(duì)象。然后調(diào)用getName()方法獲取到了私有屬性的引用,這樣就可以在其他任何線程中,修改該屬性的值。這樣將會(huì)導(dǎo)致我們?cè)谄渌€程中,獲取該屬性的值時(shí)是不確定的,因?yàn)椴⒉荒艿弥搶傩缘闹凳欠褚驯黄渌€程所修改過(guò),所以這就是不安全的對(duì)象發(fā)布。
對(duì)象逸出Demo
package com.artisan.example.publish;import com.artisan.anno.NotRecommand; import com.artisan.anno.NotThreadSafe;import lombok.extern.slf4j.Slf4j;/*** * 對(duì)象逸出示例,在對(duì)象構(gòu)造完成之前,不可以將其發(fā)布* @author yangshangwei**/ @Slf4j @NotThreadSafe @NotRecommand public class ObjectEscapeDemo {private int thisCanBeEscape = 0;public ObjectEscapeDemo() {new InnerClass();}private class InnerClass {// this引用的逸出// 內(nèi)部類的構(gòu)造器里包含了對(duì)封裝實(shí)例的隱含引用,這樣在對(duì)象沒有被正確構(gòu)造完成之前就會(huì)被發(fā)布,由此會(huì)導(dǎo)致不安全的因素在里面public InnerClass() {log.info("{}", ObjectEscapeDemo.this.thisCanBeEscape);}}public static void main(String[] args) {new ObjectEscapeDemo();}}上述代碼中,內(nèi)部類的構(gòu)造器里包含了對(duì)封裝實(shí)例的隱含引用,這樣在對(duì)象沒有被正確構(gòu)造完成之前就會(huì)被發(fā)布,由此會(huì)導(dǎo)致不安全的因素在里面。
其中一個(gè)就是導(dǎo)致this引用在構(gòu)造期間逸出的錯(cuò)誤,它是在構(gòu)造函數(shù)構(gòu)造過(guò)程中啟動(dòng)了一個(gè)線程,無(wú)論是顯式啟動(dòng)還是隱式啟動(dòng),都會(huì)造成this引用的逸出。
新線程總會(huì)在所屬對(duì)象構(gòu)造完畢之前就已經(jīng)看到它了,所以如果要在構(gòu)造函數(shù)中創(chuàng)建線程,那么不要啟動(dòng)它,而是應(yīng)該采用一個(gè)專有的start,或是其他初始化的方式統(tǒng)一啟動(dòng)線程。
這里其實(shí)我們可以使用工廠方法和私有構(gòu)造函數(shù)來(lái)完成對(duì)象創(chuàng)建和監(jiān)聽器的注冊(cè)等等來(lái)避免不正確的發(fā)布。
小結(jié)
不正確的發(fā)布可變對(duì)象導(dǎo)致的兩種錯(cuò)誤:
- 發(fā)布線程以外的所有線程都可以看到被發(fā)布對(duì)象的過(guò)期的值
- 線程看到的被發(fā)布對(duì)象的引用是最新的,然而被發(fā)布對(duì)象的狀態(tài)卻是過(guò)期的
代碼
https://github.com/yangshangwei/ConcurrencyMaster
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的并发编程-08安全发布对象之发布与逸出的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Spring Boot2.x-13前后端
- 下一篇: 并发编程-09安全发布对象+单例模式详解