arraylist线程安全吗_Java的线程安全、单例模式、JVM内存结构等知识梳理
java技術(shù)總結(jié)
知其然,不知其所以然 !在技術(shù)的海洋里,遨游!
做一個積極的人
編碼、改bug、提升自己
我有一個樂園,面向編程,春暖花開!
本篇以一些問題開頭,請先不看答案,自己思考一下,看一下你能回答上來多少! 本文內(nèi)容較多,可以收藏后查看!
思考一下
1、都說String是不可變的,為什么我可以這樣做呢? String a = "1"; a = "2";
2、HashMap的實現(xiàn)原理 ?
3、寫出三種單例模式,如果能考慮線程安全最好?
4、ArrayList和LinkedList有什么區(qū)別 ?
5、什么是線程安全,為什么會出現(xiàn)線程安全問題?
6、實現(xiàn)線程的二種方式?
7、Lock與Synchronized的區(qū)別?
8、JVM的內(nèi)存結(jié)構(gòu) ?
9、請解釋如下jvm參數(shù)的含義:
-server -Xms512m -Xmx512m -Xss1024K -XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxTenuringThreshold=20 -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly10、數(shù)據(jù)庫隔離級別有哪些,各自的含義是什么,MYSQL默認的隔離級別是是什么?
1、都說String是不可變的,為什么我可以這樣做呢,String a = "1";a = "2";
先看一段代碼,然后通過代碼和一幅圖進行講解!
public class StringTest { public static void main(String[] args) { String s = "ABCabc"; System.out.println("s1.hashCode() = " + s.hashCode() + "--" + s); s = "123456"; System.out.println("s2.hashCode() = " + s.hashCode() + "--" + s); //運行后輸出的結(jié)果不同,兩個值的hascode也不一致, //說明設(shè)置的值在內(nèi)存中存儲在不同的位置 }}【首先創(chuàng)建一個String對象s,然后讓s的值為“ABCabc”, 然后又讓s的值為“123456”。 從打印結(jié)果可以看出,s的值確實改變了。那么怎么還說String對象是不可變的呢?
其實這里存在一個誤區(qū): s只是一個String對象的引用,并不是對象本身。對象在內(nèi)存中是一塊內(nèi)存區(qū),成員變量越多,這塊內(nèi)存區(qū)占的空間越大。引用只是一個4字節(jié)的數(shù)據(jù),里面存放了它所指向的對象的地址,通過這個地址可以訪問對象。
也就是說,s只是一個引用,它指向了一個具體的對象,當s=“123456”; 這句代碼執(zhí)行過之后,又創(chuàng)建了一個新的對象“123456”, 而引用s重新指向了這個心的對象,原來的對象“ABCabc”還在內(nèi)存中存在,并沒有改變。內(nèi)存結(jié)構(gòu)如下圖所示:
---圖片摘自【Java中的String為什么是不可變的? -- String源碼分析】
- 相關(guān)參考文章
1:【知乎-胖胖 回答】如何理解 String 類型值的不可變?: https://www.zhihu.com/question/20618891
2:8 張圖理解 Java :https://mp.weixin.qq.com/s/nidDtGZ9P-YJaXSxvZPv_w
一圖講解String
2、HashMap的實現(xiàn)原理
HashMap 我之前的專欄中也有寫過,分析HashMap要注意JDK版本,jdk1.7和jdk1.8中底層的實現(xiàn)就有不同。
說簡單點HashMap是一個集合,通過put(key,value)存儲數(shù)據(jù),然后使用get(key)獲取數(shù)據(jù)。
實現(xiàn)原理是基于hashing原理,使用hash算法實現(xiàn)。 jdk1.7 數(shù)組+鏈表 jdk1.8 數(shù)組+鏈表+紅黑樹
- 詳情可參考下面博文:
- java集合系列——Map之HashMap介紹(八): http://blog.csdn.net/u010648555/article/details/60324303 HashMap的工作原理 : http://www.importnew.com/7099.html Java8系列之重新認識HashMap : http://www.importnew.com/20386.html
3、寫出三種單例模式,如果能考慮線程安全最好
首先總結(jié)目前實現(xiàn)單例模式的方式有以下五種:
- 餓漢式,線程安全
- 懶漢式,線程不安全(注意加synchronized,變線程安全)
- 雙重檢驗鎖(注意將instance 變量聲明成 volatile,并注意jdk版本大于等于1.5)
- 靜態(tài)內(nèi)部類 ,線程安全
- 枚舉,線程安全
注:推薦使用后面三種
具體代碼就不一一寫了:如果想了解具體的代碼如何寫,點擊下面:
你真的會寫單例模式嗎——Java實現(xiàn): http://www.importnew.com/18872.html
- 雙重檢驗鎖 : jdk1.5以后才能正確工作
- 靜態(tài)內(nèi)部類 : 延時加載,線程安裝
- 枚舉 : [Effective Java] 推薦盡可能地使用枚舉來實現(xiàn)單例
4 、ArrayList和LinkedList有什么區(qū)別
我之前博客也有寫過 , java集合系列——List集合總結(jié) :http://blog.csdn.net/u010648555/article/details/59708627
這里在說明一下: 簡單介紹 1 ArrayList是基于數(shù)組實現(xiàn)的,是一個數(shù)組隊列。可以動態(tài)的增加容量!
使用場景
具體分析 1.ArrayList隨機讀取的時候采用的是get(index),根據(jù)指定位置讀取元素,而LinkedList則采用size/2 ,二分法去加速一次讀取元素,效率低于ArrayList! 2.ArrayList插入時候要判斷容量,刪除時候要將數(shù)組移位,有一個復制操作,效率低于LinkList!而LinkedList直接插入,不用判斷容量,刪除的時候也是直接刪除跳轉(zhuǎn)指針節(jié)點,沒有復制的操作!
5、什么是線程安全,為什么會出現(xiàn)線程安全問題?
【參考書 :Java并發(fā)編程實戰(zhàn)】:https://book.douban.com/subject/10484692/
線程安全就是多線程訪問時,采用了加鎖機制,當一個線程訪問該類的某個數(shù)據(jù)時,進行保護,其他線程不能進行訪問直到該線程讀取完,其他線程才可使用。不會出現(xiàn)數(shù)據(jù)不一致或者數(shù)據(jù)污染。[百度百科:線程安全]
線程安全 = 線程(多個線程) + 數(shù)據(jù)一致!
思考1:為什么會出現(xiàn)線程安全問題?
從百度百科的概念可以知道,發(fā)送線程安全問題的兩個條件:
- 多線程訪問
- 訪問某個數(shù)據(jù),(這里強調(diào)一下,某個數(shù)據(jù)是實例變量即對線程是共享的)
- 這兩個條件都必須滿足,缺一不可,否則不會出現(xiàn)線程安全問題。
思考2:怎么解決線程安全問題?
通過加鎖機制,可以使用關(guān)鍵字synchronized,或者java并發(fā)包中的Lock。還有在使用集合中的類如ArrayList或者HashMap時要考慮是否存在線程安全問題,如果存在最好使用ConcurrentHashMap替代hashMap,或者使用Collections.synchronizedXXX進行封裝!
- 實例:通過一段代碼演示線程安全和非線程安全
運行結(jié)果如下,多次運行后,發(fā)現(xiàn)countUnSafe總是有重復的值,并且不按照順序輸出,最后的結(jié)果也不是10; countSafe 按照順序打印,最后的結(jié)果也是10。如果你運行了上面的代碼,可能和我執(zhí)行下面打印的不一樣,但是結(jié)論是一樣的。
countUnSafe = 3countUnSafe = 3countUnSafe = 3countUnSafe = 3countUnSafe = 3countUnSafe = 5countUnSafe = 8countUnSafe = 8countUnSafe = 6countUnSafe = 5countSafe = 1countSafe = 2countSafe = 3countSafe = 4countSafe = 5countSafe = 6countSafe = 7countSafe = 8countSafe = 9countSafe = 10線程安全說簡單了就上面這些內(nèi)容,如何深入需要知道線程的工作原理,JVM下線程是如何進行工作,為什么實例變量會存在線程安全問題,而私有變量不會出現(xiàn),這就和變量在內(nèi)存中創(chuàng)建和存儲的位置有關(guān)。下面進行簡單的說明,不會一一展開了。
在程序運行后JVM中有一個主內(nèi)存,線程在創(chuàng)建后也會有一個自己的內(nèi)存(工作內(nèi)存),會拷貝主內(nèi)存的一些數(shù)據(jù),每個線程之間能夠共享主內(nèi)存,而不能訪問其他線程的工作內(nèi)存,那么一個變量是實例變量的時候,如果沒有加鎖機制,就會出現(xiàn)線程安全問題。
比如:系統(tǒng)有線程A和線程B,這兩個線程同時訪問了addUnSafe方法,并將countUnsafe變量拷貝在自己的內(nèi)存中(countUnsafe = 0),然后進行操作,那么這兩個線程 都執(zhí)行countUnsafe++,這兩個線程的工作內(nèi)存中countUnsafe = 1;然后寫回主內(nèi)存,此時主內(nèi)存countUnsafe = 1,當另一個線程C訪問時候,C工作內(nèi)存操作的countUnsafe的值就是1,此時發(fā)生了線程安全問題。
【圖片來自--java并發(fā)編程藝術(shù)-第二章 java內(nèi)存模型抽象結(jié)構(gòu)】
JMM模型
先就講這么多了,這些在面試中是有定的深度了。后面有時間專門在深入總結(jié)。
6、實現(xiàn)線程的二種方式
Java中有兩種方式實現(xiàn)多線程,一種是繼承Thread類,一種是實現(xiàn)Runnable接口。具
- 實現(xiàn)Runnable接口
- 繼承Thread類
注意:線程的啟動是調(diào)用start()方法,而不是run()方法!
舉例并進行解釋
1.直接調(diào)用run方法實例:
public class TestTheadDemo { public static void main(String[] args) { for (int i = 0; i < 10; i++) { ThreadTest thread = new ThreadTest(); thread.run();// thread.start(); } }}class ThreadTest extends Thread{ @Override public void run() { System.out.println("當前線程 : " + Thread.currentThread().getName()); }}運行結(jié)果 :當前線程全部是main線程,相當于ThreadTest類的thread對象直接調(diào)用了run()方法。(直接調(diào)用run方法只是一個普通的單線程程式)
當前線程 : main當前線程 : main當前線程 : main當前線程 : main當前線程 : main2.調(diào)用start()方法 將上面的代碼 注釋的thread.start();打開, thread.run();注釋!
運行結(jié)果 :發(fā)現(xiàn)啟動了不同的線程進行執(zhí)行。
當前線程 : Thread-0當前線程 : Thread-5當前線程 : Thread-3當前線程 : Thread-4當前線程 : Thread-2當前線程 : Thread-1當前線程 : Thread-7當前線程 : Thread-6當前線程 : Thread-9查看start()方法的源碼中,發(fā)現(xiàn)有個地方
public synchronized void start() { //代碼省略.... try { start0();//注意這個地方,調(diào)用了native本地方法 started = true; } finally { //代碼省略.... } } //本地方法 private native void start0();總結(jié): 調(diào)用start()方法,虛擬機JVM通過執(zhí)行本地native方法start0和操作系統(tǒng)cup進行交互,此時線程并沒有正在立即執(zhí)行,而是等待cup的調(diào)度,當cpu分配給線程時間,線程才執(zhí)行run()方法!
更多相關(guān)內(nèi)容可以參考 [java多線程編程核心技術(shù): 高洪巖 ] :https://book.douban.com/subject/26555197/
7、Lock與Synchronized的區(qū)別
在Java中Lock與Synchronized都可以進行同步操作,保證線程的安全,就如上面第一問提到的,線程安全性問題。下面進行簡單的額介紹!
(1)、Synchronized的簡單介紹
synchronized同步的原理
關(guān)鍵字synchronized可以修飾方法或者以同步塊的形式來使用,它主要確保多個線程在同一時刻,只能有一個線程處于方法或者同步塊 中,保證了線程對變量訪問的可見性和排他性。
synchronized同步實現(xiàn)的基礎(chǔ)
[一張圖講解對象鎖和關(guān)鍵字synchronized修飾方法(代碼塊] :http://blog.csdn.net/u010648555/article/details/78138225
【死磕Java并發(fā)】-----深入分析synchronized的實現(xiàn)原理]http://blog.csdn.net/chenssy/article/details/54883355
注:有興趣也可以看看 volatile關(guān)鍵字!
關(guān)鍵字volatile可以用來修飾字段(成員變量),就是告知程序任何對該變量的訪問均需要從共享內(nèi)存中獲取,而對它的改變必須同步刷新回共享內(nèi)存中,保證所有線程對變量訪問的可見性。
關(guān)鍵字volatile和關(guān)鍵字synchronized均可以實現(xiàn)線程間通信!
(2)、Lock的簡單介紹
首先明確Lock是Java 5之后,在java.util.concurrent.locks包下提供了另外一種方式來實現(xiàn)同步訪問。 Lock是一個接口,其由三個具體的實現(xiàn):ReentrantLock、ReetrantReadWriteLock.ReadLock 和 ReetrantReadWriteLock.WriteLock,即重入鎖、讀鎖和寫鎖。增加Lock機制主要是因為內(nèi)置鎖存在一些功能上局限性。
Java并發(fā)編程系列之十六:Lock鎖 : http://blog.csdn.net/u011116672/article/details/51064186
區(qū)別總結(jié): 1.synchronized是Java語言的關(guān)鍵字,Lock是一個類,通過這個類可以實現(xiàn)同步訪問;
2.synchronized是在JVM層面上實現(xiàn)的,不但可以通過一些監(jiān)控工具監(jiān)控synchronized的鎖定,而且在代碼執(zhí)行時出現(xiàn)異常,JVM會自動釋放鎖定,但是使用Lock則不行,Lock是通過代碼實現(xiàn)的,要保證鎖定一定會被釋放,就必須將unLock(),最好放到finally{}中。 3.Lock有ReetrantLock(可重入鎖)實現(xiàn)類,可選的方法比synchronized多,使用更靈活。 4..并不是Lock就比synchronized一定好,因為synchronized在jdk后面的版本也在不斷優(yōu)化。在資源競爭不是很激烈的情況下,synchronized的性能要優(yōu)于ReetrantLock,但是在資源競爭很激烈的情況下,synchronized的性能會下降很多,性能不如Lock。
Lock和synchronized的區(qū)別和使用:http://www.cnblogs.com/baizhanshi/p/6419268.html
8、JVM的內(nèi)存結(jié)構(gòu)
下圖:【圖片版本-深入理解Java虛擬機:JVM高級特性與最佳實踐:周志明】
Jvm內(nèi)存模型
? 還有這個博文: JVM內(nèi)存結(jié)構(gòu)圖解 :http://blog.csdn.net/coffeelifelau/article/details/52534672
9、請解釋如下jvm參數(shù)的含義
-server -Xms512m -Xmx512m -Xss1024K -Xmn256m-XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxTenuringThreshold=20 -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly-server :服務器模式 注:JVM client模式和server模式,生產(chǎn)環(huán)境請使用-server,性能更好! [JVM client模式和Server模式的區(qū)別]:https://zhidao.baidu.com/question/1703232626938481420.html
-Xms512m :JVM初始分配的堆內(nèi)存,一般和Xmx配置成一樣以避免每次gc后JVM重新分配內(nèi)存。
-Xmx512m :JVM最大允許分配的堆內(nèi)存,按需分配
-Xss1024K :設(shè)置每個線程的堆棧大小
-Xmn256m :年輕代內(nèi)存大小,整個JVM內(nèi)存=年輕代 + 年老代 + 持久代
-XX:PermSize=256m :設(shè)置持久代(perm gen)初始值,默認物理內(nèi)存的1/64(注意:jdk 8 已經(jīng)沒有此參數(shù))
-XX:MaxPermSize=512m : 設(shè)置持久代最大值(注意:jdk 8 已經(jīng)沒有此參數(shù))
**-XX:MaxTenuringThreshold=20 ** :垃圾最大年齡
**-XX:CMSInitiatingOccupancyFraction=80 ** :使用cms作為垃圾回收 使用80%后開始CMS收集
-XX:+UseCMSInitiatingOccupancyOnly :使用手動定義初始化定義開始CMS收集
還有很多很多參數(shù)。。。。
[一個性能較好的JVM參數(shù)配置] :http://developer.51cto.com/art/201507/486162.htm
10 、數(shù)據(jù)庫隔離級別有哪些,各自的含義是什么,MYSQL默認的隔離級別是是什么?
事務指定了4種隔離級別(從弱到強分別是):
1:Read Uncommitted(讀未提交):一個事務可以讀取另一個未提交事務的數(shù)據(jù)。
2:Read Committed(讀提交):一個事務要等另一個事務提交后才能讀取數(shù)據(jù)。
3:Repeatable Read(重復讀):在開始讀取數(shù)據(jù)(事務開啟)時,不再允許修改操作。
4:Serializable(序列化):Serializable 是最高的事務隔離級別,在該級別下,事務串行化順序執(zhí)行,可以避免臟讀、不可重復讀與幻讀。
大多數(shù)數(shù)據(jù)庫默認的事務隔離級別是Read committed,比如Sql Server , Oracle。MySQL的默認隔離級別是Repeatable read。
在事務的并發(fā)操作中可能會出現(xiàn)臟讀(dirty read),不可重復讀(repeatable read),幻讀(phantom read)。可參考:[理解事務的4種隔離級別]:http://blog.csdn.net/qq_33290787/article/details/51924963
注:Mysql查詢事務隔離級別: 查詢命令總結(jié):
查看當前會話隔離級別:select @@tx_isolation;查看系統(tǒng)當前隔離級別:select @@global.tx_isolation;設(shè)置當前會話隔離級別:set session transaction isolation level repeatable read;設(shè)置當前會話隔離級別:set global transaction isolation level repeatable read;謝謝你的閱讀,如果您覺得這篇博文對你有幫助,請點贊或者喜歡,讓更多的人看到!祝你每天開心愉快!
不管做什么,只要堅持下去就會看到不一樣!在路上,不卑不亢!
博客首頁 : http://blog.csdn.net/u010648555
愿你我在人生的路上能都變成最好的自己,能夠成為一個獨擋一面的人
? 每天都在變得更好的阿飛云
總結(jié)
以上是生活随笔為你收集整理的arraylist线程安全吗_Java的线程安全、单例模式、JVM内存结构等知识梳理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: latex 分页_latex 图片跨页显
- 下一篇: java判断方法_Java常用的判断方法