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