并發(fā)測試大致分為兩類:安全性測試(不發(fā)生任何錯誤的行為)和活躍性測試(某個良好的行為終究會發(fā)生)。
安全測試 - 通常采用測試不變性條件的形式榜贴,即判斷某個類的行為是否與其他規(guī)范保持一致豌研。
活躍性測試 - 包括進展測試和無進展測試兩個方面妹田。
性能測試與活躍性測試相關(guān),主要包括:吞吐量鹃共、響應(yīng)性鬼佣、可伸縮性。
找出需要檢查的不變條件和后延條件晶衷。
[java]view plaincopy
importjava.util.concurrent.Semaphore;
publicclassBoundedBuffer?{
privatefinalSemaphore?availableItems,?availableSpaces;
privatefinalE[]?items;
privateintputPosition?=0;
privateinttakePosition?=0;
@SuppressWarnings("unchecked")
publicBoundedBuffer(intcapacity)?{
availableItems?=newSemaphore(0);
availableSpaces?=newSemaphore(capacity);
items?=?(E[])newObject[capacity];
}
publicbooleanisEmpty()?{
returnavailableItems.availablePermits()?==0;
}
publicbooleanisFull()?{
returnavailableSpaces.availablePermits()?==0;
}
publicvoidput(E?x)throwsInterruptedException?{
availableSpaces.acquire();
doInsert(x);
availableItems.release();
}
publicE?take()throwsInterruptedException?{
availableItems.acquire();
E?item?=?doExtract();
availableSpaces.release();
returnitem;
}
privatesynchronizedvoiddoInsert(E?x)?{
inti?=?putPosition;
items[i]?=?x;
putPosition?=?(++i?==?items.length)?0:?i;
}
privatesynchronizedE?doExtract()?{
inti?=?takePosition;
E?x?=?items[i];
items[i]?=null;
takePosition?=?(++i?==?items.length)?0:?i;
returnx;
}
}
1 基本的單元測試
[java]view plaincopy
importstaticorg.junit.Assert.*;
importorg.junit.Test;
publicclassBoundedBufferTests?{
@Test
publicvoidtestIsEmptyWhenConstructed()?{
BoundedBuffer?bb?=newBoundedBuffer(10);
assertTrue(bb.isEmpty());
assertFalse(bb.isFull());
}
@Test
publicvoidtestIsFullAfterPuts()throwsInterruptedException?{
BoundedBuffer?bb?=newBoundedBuffer(10);
for(inti?=0;?i?<10;?i++)?{
bb.put(i);
}
assertTrue(bb.isFull());
assertTrue(bb.isEmpty());
}
}
2 對阻塞操作的測試
take方法是否阻塞、中斷處理阴孟。從空緩存中獲取一個元素晌纫。
[java]view plaincopy
@Test
publicvoidtestTakeBlocksWhenEmpty(){
finalBoundedBuffer?bb?=newBoundedBuffer(10);
Thread?taker?=newThread(){
@Override
publicvoidrun()?{
try{
intunused?=??bb.take();
fail();//如果執(zhí)行到這里,那么表示出現(xiàn)了一個錯誤
}catch(InterruptedException?e)?{?}
}
};
try{
taker.start();
Thread.sleep(LOCKUP_DETECT_TIMEOUT);
taker.interrupt();
taker.join(LOCKUP_DETECT_TIMEOUT);
assertFalse(taker.isAlive());
}catch(InterruptedException?e)?{
fail();
}
}
創(chuàng)建一個“獲取”線程永丝,該線程將嘗試從空緩存中獲取一個元素锹漱。
如果take方法成功,那么表示測試失敗慕嚷。
執(zhí)行測試的線程啟動“獲取”線程哥牍,等待一段時間毕泌,然后中斷該線程。
如果“獲取”線程正確地在take方法中阻塞嗅辣,那么將拋出InterruptedException撼泛,而捕獲到這個異常的catch塊將把這個異常視為測試成功,并讓線程退出澡谭。
然后愿题,主測試線程會嘗試與“獲取”線程合并,通過調(diào)用Thread.isAlive來驗證join方法是否成功返回译暂,如果“獲取”線程可以響應(yīng)中斷抠忘,那么join能很快地完成。
使用Thread.getState來驗證線程能否在一個條件等待上阻塞外永,但這種方法并不可靠崎脉。被阻塞線程并不需要進入WAITING或者TIMED_WAITING等狀態(tài),因此JVM可以選擇通過自旋等待來實現(xiàn)阻塞伯顶。
3 安全性測試
在構(gòu)建對并發(fā)類的安全性測試中囚灼,需要解決地關(guān)鍵性問題在于,要找出那些容易檢查的屬性祭衩,這些屬性在發(fā)生錯誤的情況下極有可能失敗灶体,同時又不會使得錯誤檢查代碼人為地限制并發(fā)性。理想情況是掐暮,在測試屬性中不需要任何同步機制蝎抽。
[java]view plaincopy
importjava.util.concurrent.CyclicBarrier;
importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
importjava.util.concurrent.atomic.AtomicInteger;
importjunit.framework.TestCase;
publicclassPutTakeTestextendsTestCase?{
privatestaticfinalExecutorService?pool?=?Executors.newCachedThreadPool();
privatefinalAtomicInteger?putSum?=newAtomicInteger(0);
privatefinalAtomicInteger?takeSum?=newAtomicInteger(0);
privatefinalCyclicBarrier?barrier;
privatefinalBoundedBuffer?bb;
privatefinalintnTrials,?nPairs;
publicstaticvoidmain(String[]?args)?{
newPutTakeTest(10,10,100000).test();//?示例參數(shù)
pool.shutdown();
}
staticintxorShift(inty)?{
y?^=?(y?<<6);
y?^=?(y?>>>21);
y?^=?(y?<<7);
returny;
}
publicPutTakeTest(intcapacity,intnPairs,intnTrials)?{
this.bb?=newBoundedBuffer(capacity);
this.nTrials?=?nTrials;
this.nPairs?=?nPairs;
this.barrier?=newCyclicBarrier(nPairs?*2+1);
}
voidtest()?{
try{
for(inti?=0;?i?<?nPairs;?i++)?{
pool.execute(newProducer());
pool.execute(newConsumer());
}
barrier.await();//?等待所有的線程就緒
barrier.await();//?等待所有的線程執(zhí)行完成
assertEquals(putSum.get(),?takeSum.get());
}catch(Exception?e)?{
thrownewRuntimeException(e);
}
}
classProducerimplementsRunnable?{
@Override
publicvoidrun()?{
try{
intseed?=?(this.hashCode()?^?(int)?System.nanoTime());
intsum?=0;
barrier.await();
for(inti?=?nTrials;?i?>0;?--i)?{
bb.put(seed);
sum?+=?seed;
seed?=?xorShift(seed);
}
putSum.getAndAdd(sum);
barrier.await();
}catch(Exception?e)?{
thrownewRuntimeException(e);
}
}
}
classConsumerimplementsRunnable?{
@Override
publicvoidrun()?{
try{
barrier.await();
intsum?=0;
for(inti?=?nTrials;?i?>0;?--i)?{
sum?+=?bb.take();
}
takeSum.getAndAdd(sum);
barrier.await();
}catch(Exception?e)?{
thrownewRuntimeException(e);
}
}
}
}
4 資源管理的測試
對于任何持有或管理其他對象的對象,都應(yīng)該在不需要這些對象時銷毀對他們的引用路克。測試資源泄露的例子:
[java]view plaincopy
classBig?{
double[]?data?=newdouble[100000];
};
voidtestLeak()throwsInterruptedException{
BoundedBuffer?bb?=newBoundedBuffer(CAPACITY);
intheapSize1?=/*?生成堆的快照?*/;
for(inti?=0;?i?<?CAPACITY;?i++){
bb.put(newBig());
}
for(inti?=0;?i?<?CAPACITY;?i++){
bb.take();
}
intheapSize2?=/*?生成堆的快照?*/;
assertTrue(Math.abs(heapSize1?-?heapSize2)?<?THRESHOLD);
}
5 使用回調(diào)
6 產(chǎn)生更多的交替操作
性能測試的目標(biāo) - 根據(jù)經(jīng)驗值來調(diào)整各種不同的限值。例如:線程數(shù)量精算、緩存容量等瓢宦。
1 在PutTakeTest中增加計時功能
基于柵欄的定時器
[java]view plaincopy
this.timer?=newBarrierTimer();
this.barrier?=newCyclicBarrier(nPairs?*2+1,?timer);
publicclassBarrierTimerimplementsRunnable{
privatebooleanstarted?;
privatelongstartTime?;
privatelongendTime?;
@Override
publicsynchronizedvoidrun()?{
longt?=?System.nanoTime();
if(!started?){
started?=true;
startTime?=?t;
}else{
endTime?=?t;
}
}
publicsynchronizedvoidclear(){
started?=false;
}
publicsynchronizedlonggetTime(){
returnendTime?-?startTime;
}
}
修改后的test方法中使用了基于柵欄的計時器
[java]view plaincopy
voidtest(){
try{
timer.clear();
for(inti?=0;?i?<?nPairs;?i++){
pool?.execute(newProducer());
pool?.execute(newConsumer());
}
barrier?.await();
barrier?.await();
longnsPerItem?=?timer.getTime()?/?(?nPairs?*?(long)nTrials?);
System.?out?.println("Throughput:?"+?nsPerItem?+"?ns/item");
assertEquals(putSum.get(),?takeSum.get()?)
}catch(Exception?e)?{
thrownewRuntimeException(e);
}
. 生產(chǎn)者消費者模式在不同參數(shù)組合下的吞吐率
. 有界緩存在不同線程數(shù)量下的伸縮性
. 如何選擇緩存的大小
[java]view plaincopy
publicstaticvoidmain(String[]?args)throwsInterruptedException?{
inttpt?=100000;//?每個線程中的測試次數(shù)
for(intcap?=1;?cap?<=?tpt;?cap?*=10){
System.?out?.println("Capacity:?"+?cap);
for(intpairs?=1;?pairs?<=128;?pairs?*=2){
TimedPutTakeTest?t?=newTimedPutTakeTest(cap,?pairs,?tpt);
System.?out?.println("Pairs:?"+?pairs?+"\t");
t.test();
System.?out?.println("\t");
Thread.?sleep(1000);
t.test();
System.?out?.println();
Thread.?sleep(1000);
}
}
pool?.shutdown();
}
查看吞吐量/線程數(shù)量的關(guān)系
2 多種算法的比較
3 響應(yīng)性衡量
1 垃圾回收
2 動態(tài)編譯
3 對代碼路徑的不真實采樣
4 不真實的競爭程度
5 無用代碼的消除
1 代碼審查
2 靜態(tài)分析工具
FindBugs驮履、Lint
3 面向方面的測試技術(shù)
4 分析與監(jiān)測工具
以上就是我推薦給Java開發(fā)者們的一面試經(jīng)典知識。但是這些知識里面并沒有太多Java全棧廉嚼、Java晉階玫镐、JAVA架構(gòu)之類的題,不是我不推薦怠噪,而是希望大家更多的從基本功做起恐似,打好基礎(chǔ),太多復(fù)雜的內(nèi)容一會兒也說不明白舰绘。
好了同學(xué)們蹂喻,我能介紹的也都全部介紹完給你們了葱椭,如果下獲得更多JAVA教學(xué)資源,可以選擇來我們這里共同交流口四,群:240448376孵运,很多大神在這里切磋學(xué)習(xí),不懂可以直接問蔓彩,晚上還有大牛免費直播教學(xué)治笨。
注:加群要求
1、具有一定工作經(jīng)驗的赤嚼,面對目前流行的技術(shù)不知從何下手旷赖,需要突破技術(shù)瓶頸的可以加,有些應(yīng)屆生和實習(xí)生也可以加更卒。
2等孵、在公司待久了,過得很安逸蹂空,但跳槽時面試碰壁俯萌。需要在短時間內(nèi)進修、跳槽拿高薪的可以加上枕。
3咐熙、如果沒有工作經(jīng)驗,但基礎(chǔ)非常扎實辨萍,對java工作機制棋恼,常用設(shè)計思想,常用java開發(fā)框架掌握熟練的锈玉,可以加爪飘。
4、覺得自己很牛B嘲玫,一般需求都能搞定悦施。但是所學(xué)的知識點沒有系統(tǒng)化并扇,很難在技術(shù)領(lǐng)域繼續(xù)突破的可以加去团。
5.阿里Java高級大牛直播講解知識點,分享知識穷蛹,多年工作經(jīng)驗的梳理和總結(jié)土陪,帶著大家全面、科學(xué)地建立自己的技術(shù)體系和技術(shù)認(rèn)知肴熏!
PS:現(xiàn)在主要講解的內(nèi)容是(反射原理鬼雀、枚舉原理與應(yīng)用、注解原理蛙吏、常用設(shè)計模式源哩、正規(guī)表達式高級應(yīng)用鞋吉、JAVA操作Office原理詳解、JAVA圖像處理技術(shù)励烦,等多個知識點的詳解和實戰(zhàn))
6.小號或者小白之類加群一律不給過谓着,謝謝。
最后坛掠,每一位讀到這里的網(wǎng)友赊锚,感謝你們能耐心地看完。覺得對你有幫助可以給個喜歡屉栓!希望在成為一名更優(yōu)秀的Java程序員的道路上舷蒲,我們可以一起學(xué)習(xí)、一起進步