知其然,不知其所以然 基公!在技術(shù)的海洋里幅慌,前路漫漫,我一直在迷失著自我轰豆。
本文始發(fā)地址:http://blog.csdn.net/u010648555/article/details/78195674
歡迎訪問我的csdn博客胰伍,我們一同成長齿诞!
不管做什么,只要堅持下去就會看到不一樣骂租!在路上祷杈,不卑不亢!
博客首頁 :http://blog.csdn.net/u010648555 歡迎訪問!
在下面的題目來自于我要加的一個QQ群渗饮,然后要加這個QQ群但汞,首先要通過進階考核,也就是下面這些題互站,當我看到這些題目的時候私蕾。發(fā)現(xiàn)這些題目很常見,但是細細去研究胡桃,發(fā)現(xiàn)每一個問題的知識點都是特別的多也比較深奧踩叭!
1,什么是線程安全 (參考書:https://book.douban.com/subject/10484692/)
2翠胰,都說String是不可變的容贝,為什么我可以這樣做呢
String a = "1";
a = "2";
3,HashMap的實現(xiàn)原理
4之景,寫出三種單例模式嗤疯,如果能考慮線程安全最好
5,ArrayList和LinkedList有什么區(qū)別
6闺兢,實現(xiàn)線程的2種方式
7,JVM的內(nèi)存結(jié)構(gòu)
8戏罢,Lock與Synchronized的區(qū)別
9屋谭,數(shù)據(jù)庫隔離級別有哪些,各自的含義是什么龟糕,MYSQL默認的隔離級別是是什么桐磁。
10,請解釋如下jvm參數(shù)的含義:
-server -Xms512m -Xmx512m -Xss1024K
-XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxTenuringThreshold=20
-XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly讲岁。
1.什么是線程安全 (參考書:https://book.douban.com/subject/10484692/)
線程安全就是多線程訪問時我擂,采用了加鎖機制,當一個線程訪問該類的某個數(shù)據(jù)時缓艳,進行保護校摩,其他線程不能進行訪問直到該線程讀取完,其他線程才可使用阶淘。不會出現(xiàn)數(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進行封裝!
實例:通過一段代碼演示線程安全和非線程安全
/**
* 線程安全和線程不安全---簡單實例
*
* @author:dufy
* @version:1.0.0
* @date 2017/10/13
*/
public class ThreadSafey {
private int countUnSafe = 0;//實例變量
private int countSafe = 0;
//線程不安全的方法
public void addUnSafe(){
try {
Thread.sleep(100);//為了更好的測試挣轨。休眠100ms
} catch (InterruptedException e) {
e.printStackTrace();
}
countUnSafe ++;
System.out.println("countUnSafe = " + countUnSafe);
}
//線程安全的方法
//這里也可以使用使用同步代碼塊的方式军熏,建議在實際開發(fā)使用同步代碼塊,相對比同步方法好很多卷扮, 也可以使用Lock進行加鎖荡澎!
public synchronized void addSafe(){
try {
Thread.sleep(100);//為了更好的測試。休眠100ms
} catch (InterruptedException e) {
e.printStackTrace();
}
countSafe ++;
System.out.println("countSafe = " + countSafe);
}
public static void main(String[] args) {
ThreadSafey ts = new ThreadSafey();
UnSafeT unSafeT = new UnSafeT(ts);
SafeT safeT = new SafeT(ts);
//啟動10個線程
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(unSafeT);
thread.start();
}
//啟動10個線程
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(safeT);
thread.start();
}
}
}
//不安全線程測試類
class UnSafeT implements Runnable{
private ThreadSafey threadSafey;
public UnSafeT(ThreadSafey threadSafey){
this.threadSafey = threadSafey;
}
@Override
public void run() {
threadSafey.addUnSafe();
}
}
//安全線程測試類
class SafeT implements Runnable{
private ThreadSafey threadSafey;
public SafeT(ThreadSafey threadSafey){
this.threadSafey = threadSafey;
}
@Override
public void run() {
threadSafey.addSafe();
}
}
運行結(jié)果如下晤锹,多次運行后摩幔,發(fā)現(xiàn)countUnSafe總是有重復(fù)的值,并且不按照順序輸出鞭铆,最后的結(jié)果也不是10或衡;
countSafe 按照順序打印,最后的結(jié)果也是10车遂。如果你運行了上面的代碼封断,可能和我執(zhí)行下面打印的不一樣,但是結(jié)論是一樣的舶担。
countUnSafe = 3
countUnSafe = 3
countUnSafe = 3
countUnSafe = 3
countUnSafe = 3
countUnSafe = 5
countUnSafe = 8
countUnSafe = 8
countUnSafe = 6
countUnSafe = 5
countSafe = 1
countSafe = 2
countSafe = 3
countSafe = 4
countSafe = 5
countSafe = 6
countSafe = 7
countSafe = 8
countSafe = 9
countSafe = 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)】
暫時就講這么多了户侥!
可以參考:Java 線程通信內(nèi)存模型---主內(nèi)存與工作內(nèi)存 了解更多詳細內(nèi)容镀琉!
2.都說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對象是不可變的呢俗扇?】
<font color='red'>其實這里存在一個誤區(qū): s只是一個String對象的引用,并不是對象本身箕别。</font>對象在內(nèi)存中是一塊內(nèi)存區(qū)铜幽,成員變量越多,這塊內(nèi)存區(qū)占的空間越大串稀。<font color='red'>引用只是一個4字節(jié)的數(shù)據(jù)除抛,里面存放了它所指向的對象的地址,通過這個地址可以訪問對象母截。</font>
也就是說到忽,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
3.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介紹(八)
HashMap的工作原理
Java8系列之重新認識HashMap
4.寫出三種單例模式检诗,如果能考慮線程安全最好
首先總結(jié)目前實現(xiàn)單例模式的方式有以下五種:
- 餓漢式匈仗,線程安全
- 懶漢式,線程不安全(注意加synchronized逢慌,變線程安全)
- 雙重檢驗鎖(注意將instance 變量聲明成 volatile悠轩,并注意jdk版本大于等于1.5)
- 靜態(tài)內(nèi)部類 ,線程安全
- 枚舉攻泼,線程安全
注:推薦使用后面三種
具體代碼就不一一寫了:如果想了解具體的代碼如何寫火架,點擊下面:
5.ArrayList和LinkedList有什么區(qū)別
這個我之前也在我的博客中有介紹在java集合系列——List集合總結(jié)(六)
這里在說明一下:
簡單介紹
1 ArrayList是基于數(shù)組實現(xiàn)的,是一個數(shù)組隊列忙菠『渭Γ可以動態(tài)的增加容量!
- LinkedList是基于鏈表實現(xiàn)的牛欢,是一個雙向循環(huán)列表骡男。可以被當做堆棧使用傍睹!
使用場景
- 當集合中對插入元素數(shù)據(jù)的速度要求不高隔盛,但是要求快速訪問元素數(shù)據(jù),則使用ArrayList拾稳!
- 當集合中對訪問元素數(shù)據(jù)速度不做要求不高吮炕,但是對插入和刪除元素數(shù)據(jù)速度要求高的情況,則使用LinkedList访得!
具體分析
1.ArrayList隨機讀取的時候采用的是get(index),根據(jù)指定位置讀取元素龙亲,而LinkedList則采用size/2 ,二分法去加速一次讀取元素,效率低于ArrayList!
2.ArrayList插入時候要判斷容量鳄炉,刪除時候要將數(shù)組移位杜耙,有一個復(fù)制操作,效率低于LinkList迎膜!而LinkedList直接插入泥技,不用判斷容量,刪除的時候也是直接刪除跳轉(zhuǎn)指針節(jié)點磕仅,沒有復(fù)制的操作珊豹!
6.實現(xiàn)線程的2種方式
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
當前線程 : main
2.調(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ù): 高洪巖 】
7.JVM的內(nèi)存結(jié)構(gòu)
上圖:【圖片版本-深入理解Java虛擬機:JVM高級特性與最佳實踐:周志明】
點擊查看詳情: 運行時數(shù)據(jù)區(qū)域
還有這個博文: JVM內(nèi)存結(jié)構(gòu)圖解
8.Lock與Synchronized的區(qū)別
在Java中Lock與Synchronized都可以進行同步操作,保證線程的安全杠园,就如上面第一問提到的顾瞪,線程安全性問題。下面進行簡單的額介紹抛蚁!
1.Synchronized的簡單介紹
synchronized同步的原理
synchronized可以保證方法或者代碼塊在運行時玲昧,同一時刻只有一個方法可以進入到臨界區(qū),同時它還可以保證共享變量的內(nèi)存可見性
synchronized同步實現(xiàn)的基礎(chǔ)
- 普通同步方法篮绿,鎖是當前實例對象
- 靜態(tài)同步方法,鎖是當前類的class對象
- 同步方法塊吕漂,鎖是括號里面的對象
一張圖講解對象鎖和關(guān)鍵字synchronized修飾方法(代碼塊)
【死磕Java并發(fā)】-----深入分析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)置鎖存在一些功能上局限性玷犹。
區(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。
9.數(shù)據(jù)庫隔離級別有哪些偶洋,各自的含義是什么熟吏,MYSQL默認的隔離級別是是什么。
這個問題正好在上一家公司整理過玄窝,開心ing牵寺!
事務(wù)指定了4種隔離級別(從弱到強分別是):
- Read Uncommitted:讀未提交
- Read Committed:讀提交
- Repeatable Read:重復(fù)讀
- Serializable:序列化
1:Read Uncommitted(讀未提交):一個事務(wù)可以讀取另一個未提交事務(wù)的數(shù)據(jù)。
2:Read Committed(讀提交):一個事務(wù)要等另一個事務(wù)提交后才能讀取數(shù)據(jù)恩脂。
3:Repeatable Read(重復(fù)讀):在開始讀取數(shù)據(jù)(事務(wù)開啟)時帽氓,不再允許修改操作。
4:Serializable(序列化):Serializable 是最高的事務(wù)隔離級別俩块,在該級別下黎休,事務(wù)串行化順序執(zhí)行浓领,可以避免臟讀、不可重復(fù)讀與幻讀势腮。
大多數(shù)數(shù)據(jù)庫默認的事務(wù)隔離級別是Read committed联贩,比如Sql Server , Oracle。MySQL的默認隔離級別是Repeatable read捎拯。
在事務(wù)的并發(fā)操作中可能會出現(xiàn)臟讀(dirty read)泪幌,不可重復(fù)讀(repeatable read),幻讀(phantom read)署照』隼幔可參考:理解事務(wù)的4種隔離級別
注:Mysql查詢事務(wù)隔離級別:
查看當前會話隔離級別: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;
10,請解釋如下jvm參數(shù)的含義:-server -Xms512m -Xmx512m -Xss1024K -XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxTenuringThreshold=20 XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly藤树。
-server -Xms512m -Xmx512m -Xss1024K -Xmn256m
-XX:PermSize=256m -XX:MaxPermSize=512m
-XX:MaxTenuringThreshold=20
-XX:CMSInitiatingOccupancyFraction=80
-XX:+UseCMSInitiatingOccupancyOnly
【圖片來自網(wǎng)絡(luò)浴滴,如有版權(quán)問題,請反饋岁钓,及時刪除升略!】
-server :服務(wù)器模式
注:JVM client模式和server模式,生產(chǎn)環(huán)境請使用-server屡限,性能更好品嚣!
JVM client模式和Server模式的區(qū)別
-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
-XX:MaxPermSize=512m : 設(shè)置持久代最大值
-XX:MaxTenuringThreshold=20 : 垃圾最大年齡
-XX:CMSInitiatingOccupancyFraction=80 : 使用cms作為垃圾回收
使用80%后開始CMS收集</fon
-XX:+UseCMSInitiatingOccupancyOnly : 使用手動定義初始化定義開始CMS收集
還有很多很多參數(shù)眶诈。。瓜饥。逝撬。
jvm參數(shù)設(shè)置參考博文
JVM調(diào)優(yōu)總結(jié) -Xms -Xmx -Xmn -Xss
本次總結(jié)結(jié)束...........
如果帥氣(美麗)乓土、睿智(聰穎)宪潮,和我一樣簡單善良的你看到本篇博文中存在問題,請指出趣苏,我虛心接受你讓我成長的批評狡相,謝謝閱讀!
祝你今天開心愉快食磕!
歡迎訪問我的csdn博客尽棕,我們一同成長!
不管做什么彬伦,只要堅持下去就會看到不一樣滔悉!在路上蟀悦,不卑不亢!
博客首頁 :http://blog.csdn.net/u010648555 歡迎訪問!