摘要:大多數(shù)待遇豐厚的開(kāi)發(fā)職位都要求開(kāi)發(fā)者精通多線程技術(shù)并且有豐富的程序開(kāi)發(fā)調(diào)試優(yōu)化經(jīng)驗(yàn),所以線程相關(guān)的問(wèn)題在面試中經(jīng)常會(huì)被提到。掌握了這些技巧,你就可以輕松應(yīng)對(duì)多線程和并發(fā)面試了。進(jìn)入等待通行準(zhǔn)許時(shí),所提供的對(duì)象。
最近看到網(wǎng)上流傳著,各種面試經(jīng)驗(yàn)及面試題,往往都是一大堆技術(shù)題目貼上去,而沒(méi)有答案。
不管你是新程序員還是老手,你一定在面試中遇到過(guò)有關(guān)線程的問(wèn)題。Java語(yǔ)言一個(gè)重要的特點(diǎn)就是內(nèi)置了對(duì)并發(fā)的支持,讓Java大受企業(yè)和程序員的歡迎。大多數(shù)待遇豐厚的Java開(kāi)發(fā)職位都要求開(kāi)發(fā)者精通多線程技術(shù)并且有豐富的Java程序開(kāi)發(fā)、調(diào)試、優(yōu)化經(jīng)驗(yàn),所以線程相關(guān)的問(wèn)題在面試中經(jīng)常會(huì)被提到。
在典型的Java面試中, 面試官會(huì)從線程的基本概念問(wèn)起
如:為什么你需要使用線程, 如何創(chuàng)建線程,用什么方式創(chuàng)建線程比較好(比如:繼承thread類(lèi)還是調(diào)用Runnable接口),然后逐漸問(wèn)到并發(fā)問(wèn)題像在Java并發(fā)編程的過(guò)程中遇到了什么挑戰(zhàn),Java內(nèi)存模型,JDK1.5引入了哪些更高階的并發(fā)工具,并發(fā)編程常用的設(shè)計(jì)模式,經(jīng)典多線程問(wèn)題如生產(chǎn)者消費(fèi)者,哲學(xué)家就餐,讀寫(xiě)器或者簡(jiǎn)單的有界緩沖區(qū)問(wèn)題。僅僅知道線程的基本概念是遠(yuǎn)遠(yuǎn)不夠的, 你必須知道如何處理死鎖,競(jìng)態(tài)條件,內(nèi)存沖突和線程安全等并發(fā)問(wèn)題。掌握了這些技巧,你就可以輕松應(yīng)對(duì)多線程和并發(fā)面試了。
許多Java程序員在面試前才會(huì)去看面試題,這很正常。
因?yàn)槭占嬖囶}和練習(xí)很花時(shí)間,所以我從許多面試者那里收集了Java多線程和并發(fā)相關(guān)的50個(gè)熱門(mén)問(wèn)題。
關(guān)注微信公眾號(hào) "搜云庫(kù)" 獲取最新文章 【福利】公眾號(hào)后臺(tái)回復(fù) “進(jìn)群” 拉你進(jìn)微信【技術(shù)分享群】 【福利】在里面你可以認(rèn)識(shí)到很多搞技術(shù)大佬,免費(fèi)提問(wèn),互相學(xué)習(xí)下面是Java線程相關(guān)的熱門(mén)面試題,你可以用它來(lái)好好準(zhǔn)備面試。
【前25題】想進(jìn)大廠?50個(gè)多線程面試題,你會(huì)多少?(一)前25題想進(jìn)大廠?50個(gè)多線程面試題,你會(huì)多少?(一)
什么是線程?
什么是線程安全和線程不安全?
什么是自旋鎖?
什么是Java內(nèi)存模型?
什么是CAS?
什么是樂(lè)觀鎖和悲觀鎖?
什么是AQS?
什么是原子操作?在Java Concurrency API中有哪些原子類(lèi)(atomic classes)?
什么是Executors框架?
什么是阻塞隊(duì)列?如何使用阻塞隊(duì)列來(lái)實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者模型?
什么是Callable和Future?
什么是FutureTask?
什么是同步容器和并發(fā)容器的實(shí)現(xiàn)?
什么是多線程??jī)?yōu)缺點(diǎn)?
什么是多線程的上下文切換?
ThreadLocal的設(shè)計(jì)理念與作用?
ThreadPool(線程池)用法與優(yōu)勢(shì)?
Concurrent包里的其他東西:ArrayBlockingQueue、CountDownLatch等等。
synchronized和ReentrantLock的區(qū)別?
Semaphore有什么作用?
Java Concurrency API中的Lock接口(Lock interface)是什么?對(duì)比同步它有什么優(yōu)勢(shì)?
Hashtable的size()方法中明明只有一條語(yǔ)句”return count”,為什么還要做同步?
ConcurrentHashMap的并發(fā)度是什么?
ReentrantReadWriteLock讀寫(xiě)鎖的使用?
CyclicBarrier和CountDownLatch的用法及區(qū)別?
LockSupport工具?
Condition接口及其實(shí)現(xiàn)原理?
Fork/Join框架的理解?
wait()和sleep()的區(qū)別?
線程的五個(gè)狀態(tài)(五種狀態(tài),創(chuàng)建、就緒、運(yùn)行、阻塞和死亡)?
start()方法和run()方法的區(qū)別?
Runnable接口和Callable接口的區(qū)別?
volatile關(guān)鍵字的作用?
Java中如何獲取到線程dump文件?
線程和進(jìn)程有什么區(qū)別?
線程實(shí)現(xiàn)的方式有幾種(四種)?
高并發(fā)、任務(wù)執(zhí)行時(shí)間短的業(yè)務(wù)怎樣使用線程池?并發(fā)不高、任務(wù)執(zhí)行時(shí)間長(zhǎng)的業(yè)務(wù)怎樣使用線程池?并發(fā)高、業(yè)務(wù)執(zhí)行時(shí)間長(zhǎng)的業(yè)務(wù)怎樣使用線程池?
如果你提交任務(wù)時(shí),線程池隊(duì)列已滿,這時(shí)會(huì)發(fā)生什么?
鎖的等級(jí):方法鎖、對(duì)象鎖、類(lèi)鎖?
如果同步塊內(nèi)的線程拋出異常會(huì)發(fā)生什么?
并發(fā)編程(concurrency)并行編程(parallellism)有什么區(qū)別?
如何保證多線程下 i++ 結(jié)果正確?
一個(gè)線程如果出現(xiàn)了運(yùn)行時(shí)異常會(huì)怎么樣?
如何在兩個(gè)線程之間共享數(shù)據(jù)?
生產(chǎn)者消費(fèi)者模型的作用是什么?
怎么喚醒一個(gè)阻塞的線程?
Java中用到的線程調(diào)度算法是什么
單例模式的線程安全性?
線程類(lèi)的構(gòu)造方法、靜態(tài)塊是被哪個(gè)線程調(diào)用的?
同步方法和同步塊,哪個(gè)是更好的選擇?
如何檢測(cè)死鎖?怎么預(yù)防死鎖?
【前25題】想進(jìn)大廠?50個(gè)多線程面試題,你會(huì)多少?(一)前25題 想進(jìn)大廠?50個(gè)多線程面試題,你會(huì)多少?(一)
CyclicBarrier和CountDownLatch的用法及區(qū)別?CyclicBarrier和CountDownLatch 都位于java.util.concurrent 這個(gè)包下
CountDownLatch | CyclicBarrier |
---|---|
減計(jì)數(shù)方式 | 加計(jì)數(shù)方式 |
計(jì)算為0時(shí)釋放所有等待的線程 | 計(jì)數(shù)達(dá)到指定值時(shí)釋放所有等待線程 |
計(jì)數(shù)為0時(shí),無(wú)法重置 | 計(jì)數(shù)達(dá)到指定值時(shí),計(jì)數(shù)置為0重新開(kāi)始 |
調(diào)用countDown()方法計(jì)數(shù)減一,調(diào)用await()方法只進(jìn)行阻塞,對(duì)計(jì)數(shù)沒(méi)任何影響 | 調(diào)用await()方法計(jì)數(shù)加1,若加1后的值不等于構(gòu)造方法的值,則線程阻塞 |
不可重復(fù)利用 | 可重復(fù)利用 |
CountDownLatch類(lèi)只提供了一個(gè)構(gòu)造器:
public CountDownLatch(int count) { }; //參數(shù)count為計(jì)數(shù)值
然后下面這3個(gè)方法是CountDownLatch類(lèi)中最重要的方法:
public void await() throws InterruptedException { }; //調(diào)用await()方法的線程會(huì)被掛起,它會(huì)等待直到count值為0才繼續(xù)執(zhí)行 public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; //和await()類(lèi)似,只不過(guò)等待一定的時(shí)間后count值還沒(méi)變?yōu)?的話就會(huì)繼續(xù)執(zhí)行 public void countDown() { }; //將count值減1
CountDownLatch, 一個(gè)同步輔助類(lèi),在完成一組正在其他線程中執(zhí)行的操作之前,它允許一個(gè)或多個(gè)線程一直等待。
下面舉個(gè)例子說(shuō)明:
package main.java.CountDownLatch; import java.util.concurrent.CountDownLatch; /** * PROJECT_NAME:downLoad * Author:lucaifang * Date:2016/3/18 */ public class countDownlatchTest { public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(5); for(int i=0;i<5;i++){ new Thread(new readNum(i,countDownLatch)).start(); } countDownLatch.await(); System.out.println("線程執(zhí)行結(jié)束。。。。"); } static class readNum implements Runnable{ private int id; private CountDownLatch latch; public readNum(int id,CountDownLatch latch){ this.id = id; this.latch = latch; } @Override public void run() { synchronized (this){ System.out.println("id:"+id); latch.countDown(); System.out.println("線程組任務(wù)"+id+"結(jié)束,其他任務(wù)繼續(xù)"); } } } }
輸出結(jié)果:
id:1 線程組任務(wù)1結(jié)束,其他任務(wù)繼續(xù) id:0 線程組任務(wù)0結(jié)束,其他任務(wù)繼續(xù) id:2 線程組任務(wù)2結(jié)束,其他任務(wù)繼續(xù) id:3 線程組任務(wù)3結(jié)束,其他任務(wù)繼續(xù) id:4 線程組任務(wù)4結(jié)束,其他任務(wù)繼續(xù) 線程執(zhí)行結(jié)束。。。。
線程在countDown()之后,會(huì)繼續(xù)執(zhí)行自己的任務(wù)
二、CyclicBarrier用法CyclicBarrier會(huì)在所有線程任務(wù)結(jié)束之后,才會(huì)進(jìn)行后續(xù)任務(wù),具體可以看下面例子。
CyclicBarrier提供2個(gè)構(gòu)造器:
public CyclicBarrier(int parties, Runnable barrierAction) { } public CyclicBarrier(int parties) { }
參數(shù)parties指讓多少個(gè)線程或者任務(wù)等待至barrier狀態(tài);參數(shù)barrierAction為當(dāng)這些線程都達(dá)到barrier狀態(tài)時(shí)會(huì)執(zhí)行的內(nèi)容。
CyclicBarrier中最重要的方法就是await方法
//掛起當(dāng)前線程,直至所有線程都到達(dá)barrier狀態(tài)再同時(shí)執(zhí)行后續(xù)任務(wù); public int await() throws InterruptedException, BrokenBarrierException { }; //讓這些線程等待至一定的時(shí)間,如果還有線程沒(méi)有到達(dá)barrier狀態(tài)就直接讓到達(dá)barrier的線程執(zhí)行后續(xù)任務(wù) public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };
舉例說(shuō)明
package main.java.countOff; import java.util.concurrent.CyclicBarrier; /** * PROJECT_NAME:downLoad * Author:lucaifang * Date:2016/3/18 */ public class cyclicBarrierTest { public static void main(String[] args) throws InterruptedException { CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() { @Override public void run() { System.out.println("線程組執(zhí)行結(jié)束"); } }); for (int i = 0; i < 5; i++) { new Thread(new readNum(i,cyclicBarrier)).start(); } //CyclicBarrier 可以重復(fù)利用, // 這個(gè)是CountDownLatch做不到的 // for (int i = 11; i < 16; i++) { // new Thread(new readNum(i,cyclicBarrier)).start(); // } } static class readNum implements Runnable{ private int id; private CyclicBarrier cyc; public readNum(int id,CyclicBarrier cyc){ this.id = id; this.cyc = cyc; } @Override public void run() { synchronized (this){ System.out.println("id:"+id); try { cyc.await(); System.out.println("線程組任務(wù)" + id + "結(jié)束,其他任務(wù)繼續(xù)"); } catch (Exception e) { e.printStackTrace(); } } } } }
輸出結(jié)果:
id:1 id:2 id:4 id:0 id:3 線程組執(zhí)行結(jié)束 線程組任務(wù)3結(jié)束,其他任務(wù)繼續(xù) 線程組任務(wù)1結(jié)束,其他任務(wù)繼續(xù) 線程組任務(wù)4結(jié)束,其他任務(wù)繼續(xù) 線程組任務(wù)0結(jié)束,其他任務(wù)繼續(xù) 線程組任務(wù)2結(jié)束,其他任務(wù)繼續(xù)
http://blog.csdn.net/tolcf/article/details/50925145
LockSupport工具?1、LockSupport基本介紹與基本使用
LockSupport是JDK中比較底層的類(lèi),用來(lái)創(chuàng)建鎖和其他同步工具類(lèi)的基本線程阻塞。java鎖和同步器框架的核心 AQS: AbstractQueuedSynchronizer,就是通過(guò)調(diào)用 LockSupport .park()和 LockSupport .unpark()實(shí)現(xiàn)線程的阻塞和喚醒 的。
LockSupport 很類(lèi)似于二元信號(hào)量(只有1個(gè)許可證可供使用),如果這個(gè)許可還沒(méi)有被占用,當(dāng)前線程獲取許可并繼 續(xù) 執(zhí)行;如果許可已經(jīng)被占用,當(dāng)前線 程阻塞,等待獲取許可。
全部操作:park()/park(Object)
等待通行準(zhǔn)許。
parkNanos(long)/parkNanos(Object, long)
在指定運(yùn)行時(shí)間(即相對(duì)時(shí)間)內(nèi),等待通行準(zhǔn)許。
parkUntil(long)/parkUntil(Object, long)
在指定到期時(shí)間(即絕對(duì)時(shí)間)內(nèi),等待通行準(zhǔn)許。
unpark(Thread)
發(fā)放通行準(zhǔn)許或提前發(fā)放。(注:不管提前發(fā)放多少次,只用于一次性使用。)
getBlocker(Thread)
進(jìn)入等待通行準(zhǔn)許時(shí),所提供的對(duì)象。
主要用途:當(dāng)前線程需要喚醒另一個(gè)線程,但是只確定它會(huì)進(jìn)入阻塞,但不確定它是否已經(jīng)進(jìn)入阻塞,因此不管是否已經(jīng)進(jìn)入阻塞,還是準(zhǔn)備進(jìn)入阻塞,都將發(fā)放一個(gè)通行準(zhǔn)許。
正確用法:把LockSupport視為一個(gè)sleep()來(lái)用,只是sleep()是定時(shí)喚醒,LockSupport既可以定時(shí)喚醒,也可以由其它線程喚醒。
public static void main(String[] args) { LockSupport.park(); System.out.println("block."); }
運(yùn)行該代碼,可以發(fā)現(xiàn)主線程一直處于阻塞狀態(tài)。因?yàn)?許可默認(rèn)是被占用的 ,調(diào)用park()時(shí)獲取不到許可,所以進(jìn)入阻塞狀態(tài)。
如下代碼:先釋放許可,再獲取許可,主線程能夠正常終止。LockSupport許可的獲取和釋放,一般來(lái)說(shuō)是對(duì)應(yīng)的,如果多次unpark,只有一次park也不會(huì)出現(xiàn)什么問(wèn)題,結(jié)果是許可處于可用狀態(tài)。
public static void main(String[] args) { Thread thread = Thread.currentThread(); LockSupport.unpark(thread);//釋放許可 LockSupport.park();// 獲取許可 System.out.println("b"); }
LockSupport是不可重入 的,如果一個(gè)線程連續(xù)2次調(diào)用 LockSupport .park(),那么該線程一定會(huì)一直阻塞下去。
public static void main(String[] args) throws Exception { Thread thread = Thread.currentThread(); LockSupport.unpark(thread); System.out.println("a"); LockSupport.park(); System.out.println("b"); LockSupport.park(); System.out.println("c"); }
這段代碼打印出a和b,不會(huì)打印c,因?yàn)榈诙握{(diào)用park的時(shí)候,線程無(wú)法獲取許可出現(xiàn)死鎖。
LockSupport基本介紹與基本使用
https://www.cnblogs.com/hvicen/p/6217303.html
LockSupport基本介紹與基本使用
http://www.tianshouzhi.com/api/tutorials/mutithread/303
Condition接口及其實(shí)現(xiàn)原理?在java.util.concurrent包中,有兩個(gè)很特殊的工具類(lèi),Condition和ReentrantLock,使用過(guò)的人都知道,ReentrantLock(重入鎖)是jdk的concurrent包提供的一種獨(dú)占鎖的實(shí)現(xiàn)
我們知道在線程的同步時(shí)可以使一個(gè)線程阻塞而等待一個(gè)信號(hào),同時(shí)放棄鎖使其他線程可以能競(jìng)爭(zhēng)到鎖
在synchronized中我們可以使用Object的wait()和notify方法實(shí)現(xiàn)這種等待和喚醒
但是在Lock中怎么實(shí)現(xiàn)這種wait和notify呢?
答案是Condition,學(xué)習(xí)Condition主要是為了方便以后學(xué)習(xí)blockqueue和concurrenthashmap的源碼,同時(shí)也進(jìn)一步理解ReentrantLock。
ReentrantLock和Condition的使用方式通常是這樣的:
public static void main(String[] args) { final ReentrantLock reentrantLock = new ReentrantLock(); final Condition condition = reentrantLock.newCondition(); Thread thread = new Thread((Runnable) () -> { try { reentrantLock.lock(); System.out.println("我要等一個(gè)新信號(hào)" + this); condition.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("拿到一個(gè)信號(hào)!!" + this); reentrantLock.unlock(); }, "waitThread1"); thread.start(); Thread thread1 = new Thread((Runnable) () -> { reentrantLock.lock(); System.out.println("我拿到鎖了"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } condition.signalAll(); System.out.println("我發(fā)了一個(gè)信號(hào)??!"); reentrantLock.unlock(); }, "signalThread"); thread1.start(); }
運(yùn)行后,結(jié)果如下:
我要等一個(gè)新信號(hào)lock.ReentrantLockTest$1@a62fc3 我拿到鎖了 我發(fā)了一個(gè)信號(hào)??! 拿到一個(gè)信號(hào)!!
可以看到
Condition的執(zhí)行方式,是當(dāng)在線程1中調(diào)用await方法后,線程1將釋放鎖,并且將自己沉睡,等待喚醒,
線程2獲取到鎖后,開(kāi)始做事,完畢后,調(diào)用Condition的signal方法,喚醒線程1,線程1恢復(fù)執(zhí)行。
以上說(shuō)明Condition是一個(gè)多線程間協(xié)調(diào)通信的工具類(lèi),使得某個(gè),或者某些線程一起等待某個(gè)條件(Condition),只有當(dāng)該條件具備( signal 或者 signalAll方法被帶調(diào)用)時(shí) ,這些等待線程才會(huì)被喚醒,從而重新?tīng)?zhēng)奪鎖。
Condition自己也維護(hù)了一個(gè)隊(duì)列,該隊(duì)列的作用是維護(hù)一個(gè)等待signal信號(hào)的隊(duì)列,兩個(gè)隊(duì)列的作用是不同,事實(shí)上,每個(gè)線程也僅僅會(huì)同時(shí)存在以上兩個(gè)隊(duì)列中的一個(gè),流程是這樣的
線程1調(diào)用reentrantLock.lock時(shí),線程被加入到AQS的等待隊(duì)列中。
線程1調(diào)用await方法被調(diào)用時(shí),該線程從AQS中移除,對(duì)應(yīng)操作是鎖的釋放。
接著馬上被加入到Condition的等待隊(duì)列中,以為著該線程需要signal信號(hào)。
線程2,因?yàn)榫€程1釋放鎖的關(guān)系,被喚醒,并判斷可以獲取鎖,于是線程2獲取鎖,并被加入到AQS的等待隊(duì)列中。
線程2調(diào)用signal方法,這個(gè)時(shí)候Condition的等待隊(duì)列中只有線程1一個(gè)節(jié)點(diǎn),于是它被取出來(lái),并被加入到AQS的等待隊(duì)列中。 注意,這個(gè)時(shí)候,線程1 并沒(méi)有被喚醒。
signal方法執(zhí)行完畢,線程2調(diào)用reentrantLock.unLock()方法,釋放鎖。這個(gè)時(shí)候因?yàn)锳QS中只有線程1,于是,AQS釋放鎖后按從頭到尾的順序喚醒線程時(shí),線程1被喚醒,于是線程1回復(fù)執(zhí)行。
直到釋放所整個(gè)過(guò)程執(zhí)行完畢。
可以看到,整個(gè)協(xié)作過(guò)程是靠結(jié)點(diǎn)在AQS的等待隊(duì)列和Condition的等待隊(duì)列中來(lái)回移動(dòng)實(shí)現(xiàn)的,Condition作為一個(gè)條件類(lèi),很好的自己維護(hù)了一個(gè)等待信號(hào)的隊(duì)列,并在適時(shí)的時(shí)候?qū)⒔Y(jié)點(diǎn)加入到AQS的等待隊(duì)列中來(lái)實(shí)現(xiàn)的喚醒操作。
怎么理解Condition
http://www.importnew.com/9281.html
深入理解Condition
https://www.jianshu.com/p/6b5aa7b7684c
Fork/Join框架的理解? Fork/Join是什么Oracle的官方給出的定義是:Fork/Join框架是一個(gè)實(shí)現(xiàn)了ExecutorService接口的多線程處理器。它可以把一個(gè)大的任務(wù)劃分為若干個(gè)小的任務(wù)并發(fā)執(zhí)行,充分利用可用的資源,進(jìn)而提高應(yīng)用的執(zhí)行效率。
我們?cè)偻ㄟ^(guò)Fork和Join這兩個(gè)單詞來(lái)理解下Fork/Join框架,F(xiàn)ork就是把一個(gè)大任務(wù)切分為若干子任務(wù)并行的執(zhí)行,Join就是合并這些子任務(wù)的執(zhí)行結(jié)果,最后得到這個(gè)大任務(wù)的結(jié)果。
比如計(jì)算1+2+。。+10000,可以分割成10個(gè)子任務(wù),每個(gè)子任務(wù)分別對(duì)1000個(gè)數(shù)進(jìn)行求和,最終匯總這10個(gè)子任務(wù)的結(jié)果。
工作竊取算法工作竊取算法是指線程從其他任務(wù)隊(duì)列中竊取任務(wù)執(zhí)行(可能你會(huì)很詫異,這個(gè)算法有什么用。待會(huì)你就知道了)??紤]下面這種場(chǎng)景:有一個(gè)很大的計(jì)算任務(wù),為了減少線程的競(jìng)爭(zhēng),會(huì)將這些大任務(wù)切分為小任務(wù)并分在不同的隊(duì)列等待執(zhí)行,然后為每個(gè)任務(wù)隊(duì)列創(chuàng)建一個(gè)線程執(zhí)行隊(duì)列的任務(wù)。那么問(wèn)題來(lái)了,有的線程可能很快就執(zhí)行完了,而其他線程還有任務(wù)沒(méi)執(zhí)行完,執(zhí)行完的線程與其空閑下來(lái)不如幫助其他線程執(zhí)行任務(wù),這樣也能加快執(zhí)行進(jìn)程。所以,執(zhí)行完的空閑線程從其他隊(duì)列的尾部竊取任務(wù)執(zhí)行,而被竊取任務(wù)的線程則從隊(duì)列的頭部取任務(wù)執(zhí)行(這里使用了雙端隊(duì)列,既不影響被竊取任務(wù)的執(zhí)行過(guò)程又能加快執(zhí)行進(jìn)度)。
從以上的介紹中,能夠發(fā)現(xiàn)工作竊取算法的優(yōu)點(diǎn)是充分利用線程提高并行執(zhí)行的進(jìn)度。當(dāng)然缺點(diǎn)是在某些情況下仍然存在競(jìng)爭(zhēng),比如雙端隊(duì)列只有任務(wù)需要執(zhí)行的時(shí)候
使用Fork/Join框架分為兩步:分割任務(wù):首先需要?jiǎng)?chuàng)建一個(gè)ForkJoin任務(wù),執(zhí)行該類(lèi)的fork方法可以對(duì)任務(wù)不斷切割,直到分割的子任務(wù)足夠小
合并任務(wù)執(zhí)行結(jié)果:子任務(wù)執(zhí)行的結(jié)果同一放在一個(gè)隊(duì)列中,通過(guò)啟動(dòng)一個(gè)線程從隊(duì)列中取執(zhí)行結(jié)果。
Fork/Join實(shí)現(xiàn)了ExecutorService,所以它的任務(wù)也需要放在線程池中執(zhí)行。它的不同在于它使用了工作竊取算法,空閑的線程可以從滿負(fù)荷的線程中竊取任務(wù)來(lái)幫忙執(zhí)行。
shitong下面是計(jì)算1+2+3+4為例演示如何使用使用Fork/Join框架:
package com.rhwayfun.concurrency.r0406; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.concurrent.RecursiveTask; /** * Created by rhwayfun on 16-4-6. */ public class CountTask extends RecursiveTask{ //閾值 private static final int THRESHOLD = 2; //起始值 private int start; //結(jié)束值 private int end; public CountTask(int start, int end) { this.start = start; this.end = end; } @Override protected Integer compute() { boolean compute = (end - start) <= THRESHOLD; int res = 0; if (compute){ for (int i = start; i <= end; i++){ res += i; } }else { //如果長(zhǎng)度大于閾值,則分割為小任務(wù) int mid = (start + end) / 2; CountTask task1 = new CountTask(start,mid); CountTask task2 = new CountTask(mid + 1, end); //計(jì)算小任務(wù)的值 task1.fork(); task2.fork(); //得到兩個(gè)小任務(wù)的值 int task1Res = task1.join(); int task2Res = task2.join(); res = task1Res + task2Res; } return res; } public static void main(String[] args) throws ExecutionException, InterruptedException { ForkJoinPool pool = new ForkJoinPool(); CountTask task = new CountTask(1,5); ForkJoinTask submit = pool.submit(task); System.out.println("Final result:" + submit.get()); } }
代碼執(zhí)行結(jié)果為:
15
代碼中使用了FokJoinTask,其與一般任務(wù)的區(qū)別在于它需要實(shí)現(xiàn)compute方法,在方法需要判斷任務(wù)是否在閾值區(qū)間內(nèi),如果不是則需要把任務(wù)切分到足夠小,直到能夠進(jìn)行計(jì)算。
每個(gè)被切分的子任務(wù)又會(huì)重新進(jìn)入compute方法,再繼續(xù)判斷是否需要繼續(xù)切分,如果不需要?jiǎng)t直接得到子任務(wù)執(zhí)行的結(jié)果,如果需要的話則繼續(xù)切分,如此循環(huán),直到調(diào)用join方法得到最終的結(jié)果。
**
可以發(fā)現(xiàn)Fork/Join框架的需要把提交給ForkJoinPool,F(xiàn)orkJoinPool由ForkJoinTask數(shù)組和ForkJoinWorkerThread數(shù)組組成,前者負(fù)責(zé)將存放程序提交給ForkJoinPool的任務(wù),后者則負(fù)責(zé)執(zhí)行這些任務(wù)。關(guān)鍵在于在于fork方法與join方法**
Java并發(fā)編程系列之二十:Fork/Join框架
http://blog.csdn.net/u011116672/article/details/51073683
wait()和sleep()的區(qū)別?sleep()
方法是線程類(lèi)(Thread)的靜態(tài)方法,讓調(diào)用線程進(jìn)入睡眠狀態(tài),讓出執(zhí)行機(jī)會(huì)給其他線程,等到休眠時(shí)間結(jié)束后,線程進(jìn)入就緒狀態(tài)和其他線程一起競(jìng)爭(zhēng)cpu的執(zhí)行時(shí)間。
因?yàn)閟leep() 是static靜態(tài)的方法,他不能改變對(duì)象的機(jī)鎖,當(dāng)一個(gè)synchronized塊中調(diào)用了sleep() 方法,線程雖然進(jìn)入休眠,但是對(duì)象的機(jī)鎖沒(méi)有被釋放,其他線程依然無(wú)法訪問(wèn)這個(gè)對(duì)象。
wait()
wait()是Object類(lèi)的方法,當(dāng)一個(gè)線程執(zhí)行到wait方法時(shí),它就進(jìn)入到一個(gè)和該對(duì)象相關(guān)的等待池,同時(shí)釋放對(duì)象的機(jī)鎖,使得其他線程能夠訪問(wèn),可以通過(guò)notify,notifyAll方法來(lái)喚醒等待的線程
線程的五個(gè)狀態(tài)(五種狀態(tài),創(chuàng)建、就緒、運(yùn)行、阻塞和死亡)?線程通常都有五種狀態(tài),創(chuàng)建、就緒、運(yùn)行、阻塞和死亡。
第一是創(chuàng)建狀態(tài)。在生成線程對(duì)象,并沒(méi)有調(diào)用該對(duì)象的start方法,這是線程處于創(chuàng)建狀態(tài)。
第二是就緒狀態(tài)。當(dāng)調(diào)用了線程對(duì)象的start方法之后,該線程就進(jìn)入了就緒狀態(tài),但是此時(shí)線程調(diào)度程序還沒(méi)有把該線程設(shè)置為當(dāng)前線程,此時(shí)處于就緒狀態(tài)。在線程運(yùn)行之后,從等待或者睡眠中回來(lái)之后,也會(huì)處于就緒狀態(tài)。
第三是運(yùn)行狀態(tài)。線程調(diào)度程序?qū)⑻幱诰途w狀態(tài)的線程設(shè)置為當(dāng)前線程,此時(shí)線程就進(jìn)入了運(yùn)行狀態(tài),開(kāi)始運(yùn)行run函數(shù)當(dāng)中的代碼。
第四是阻塞狀態(tài)。線程正在運(yùn)行的時(shí)候,被暫停,通常是為了等待某個(gè)時(shí)間的發(fā)生(比如說(shuō)某項(xiàng)資源就緒)之后再繼續(xù)運(yùn)行。sleep,suspend,wait等方法都可以導(dǎo)致線程阻塞。
第五是死亡狀態(tài)。如果一個(gè)線程的run方法執(zhí)行結(jié)束或者調(diào)用stop方法后,該線程就會(huì)死亡。對(duì)于已經(jīng)死亡的線程,無(wú)法再使用start方法令其進(jìn)入就緒
start()方法和run()方法的區(qū)別?
每個(gè)線程都是通過(guò)某個(gè)特定Thread對(duì)象所對(duì)應(yīng)的方法run()來(lái)完成其操作的,方法run()稱(chēng)為線程體。通過(guò)調(diào)用Thread類(lèi)的start()方法來(lái)啟動(dòng)一個(gè)線程。
start()方法來(lái)啟動(dòng)一個(gè)線程,真正實(shí)現(xiàn)了多線程運(yùn)行。這時(shí)無(wú)需等待run方法體代碼執(zhí)行完畢,可以直接繼續(xù)執(zhí)行下面的代碼;
這時(shí)此線程是處于就緒狀態(tài), 并沒(méi)有運(yùn)行。 然后通過(guò)此Thread類(lèi)調(diào)用方法run()來(lái)完成其運(yùn)行狀態(tài), 這里方法run()稱(chēng)為線程體,它包含了要執(zhí)行的這個(gè)線程的內(nèi)容, Run方法運(yùn)行結(jié)束, 此線程終止。然后CPU再調(diào)度其它線程。
run()方法是在本線程里的,只是線程里的一個(gè)函數(shù),而不是多線程的。
如果直接調(diào)用run(),其實(shí)就相當(dāng)于是調(diào)用了一個(gè)普通函數(shù)而已,直接待用run()方法必須等待run()方法執(zhí)行完畢才能執(zhí)行下面的代碼,所以執(zhí)行路徑還是只有一條,根本就沒(méi)有線程的特征,所以在多線程執(zhí)行時(shí)要使用start()方法而不是run()方法。
有點(diǎn)深的問(wèn)題了,也看出一個(gè)Java程序員學(xué)習(xí)知識(shí)的廣度。
Runnable接口中的run()方法的返回值是void,它做的事情只是純粹地去執(zhí)行run()方法中的代碼而已;
Callable接口中的call()方法是有返回值的,是一個(gè)泛型,和Future、FutureTask配合可以用來(lái)獲取異步執(zhí)行的結(jié)果。
這其實(shí)是很有用的一個(gè)特性,因?yàn)槎嗑€程相比單線程更難、更復(fù)雜的一個(gè)重要原因就是因?yàn)槎嗑€程充滿著未知性,某條線程是否執(zhí)行了?某條線程執(zhí)行了多久?某條線程執(zhí)行的時(shí)候我們期望的數(shù)據(jù)是否已經(jīng)賦值完畢?無(wú)法得知,我們能做的只是等待這條多線程的任務(wù)執(zhí)行完畢而已。而Callable+Future/FutureTask卻可以獲取多線程運(yùn)行的結(jié)果,可以在等待時(shí)間太長(zhǎng)沒(méi)獲取到需要的數(shù)據(jù)的情況下取消該線程的任務(wù),真的是非常有用。
volatile關(guān)鍵字的作用?volatile關(guān)鍵字的作用主要有兩個(gè):
(1)多線程主要圍繞可見(jiàn)性和原子性?xún)蓚€(gè)特性而展開(kāi),使用volatile關(guān)鍵字修飾的變量,保證了其在多線程之間的可見(jiàn)性,即每次讀取到volatile變量,一定是最新的數(shù)據(jù)
(2)代碼底層執(zhí)行不像我們看到的高級(jí)語(yǔ)言—-Java程序這么簡(jiǎn)單,它的執(zhí)行是Java代碼–>字節(jié)碼–>根據(jù)字節(jié)碼執(zhí)行對(duì)應(yīng)的C/C++代碼–>C/C++代碼被編譯成匯編語(yǔ)言–>和硬件電路交互,現(xiàn)實(shí)中,為了獲取更好的性能JVM可能會(huì)對(duì)指令進(jìn)行重排序,多線程下可能會(huì)出現(xiàn)一些意想不到的問(wèn)題。使用volatile則會(huì)對(duì)禁止語(yǔ)義重排序,當(dāng)然這也一定程度上降低了代碼執(zhí)行效率
從實(shí)踐角度而言,volatile的一個(gè)重要作用就是和CAS結(jié)合,保證了原子性,詳細(xì)的可以參見(jiàn)java.util.concurrent.atomic包下的類(lèi),比如AtomicInteger。
Java中如何獲取到線程dump文件?死循環(huán)、死鎖、阻塞、頁(yè)面打開(kāi)慢等問(wèn)題,打線程dump是最好的解決問(wèn)題的途徑。所謂線程dump也就是線程堆棧,獲取到線程堆棧有兩步:
(1)獲取到線程的pid,可以通過(guò)使用jps命令,在Linux環(huán)境下還可以使用ps -ef | grep java
(2)打印線程堆棧,可以通過(guò)使用jstack pid命令,在Linux環(huán)境下還可以使用kill -3 pid
另外提一點(diǎn),Thread類(lèi)提供了一個(gè)getStackTrace()方法也可以用于獲取線程堆棧。這是一個(gè)實(shí)例方法,因此此方法是和具體線程實(shí)例綁定的,每次獲取獲取到的是具體某個(gè)線程當(dāng)前運(yùn)行的堆棧,
虛擬機(jī)性能監(jiān)控與故障處理工具 詳解
http://www.ymq.io/2017/08/01/jvm-4/
線程和進(jìn)程有什么區(qū)別?進(jìn)程是系統(tǒng)進(jìn)行資源分配的基本單位,有獨(dú)立的內(nèi)存地址空間
線程是CPU獨(dú)立運(yùn)行和獨(dú)立調(diào)度的基本單位,沒(méi)有多帶帶地址空間,有獨(dú)立的棧,局部變量,寄存器, 程序計(jì)數(shù)器等。
創(chuàng)建進(jìn)程的開(kāi)銷(xiāo)大,包括創(chuàng)建虛擬地址空間等需要大量系統(tǒng)資源
創(chuàng)建線程開(kāi)銷(xiāo)小,基本上只有一個(gè)內(nèi)核對(duì)象和一個(gè)堆棧。
一個(gè)進(jìn)程無(wú)法直接訪問(wèn)另一個(gè)進(jìn)程的資源;同一進(jìn)程內(nèi)的多個(gè)線程共享進(jìn)程的資源。
進(jìn)程切換開(kāi)銷(xiāo)大,線程切換開(kāi)銷(xiāo)?。贿M(jìn)程間通信開(kāi)銷(xiāo)大,線程間通信開(kāi)銷(xiāo)小。
線程屬于進(jìn)程,不能獨(dú)立執(zhí)行。每個(gè)進(jìn)程至少要有一個(gè)線程,成為主線程
線程實(shí)現(xiàn)的方式有幾種(四種)?繼承Thread類(lèi),重寫(xiě)run方法
實(shí)現(xiàn)Runnable接口,重寫(xiě)run方法,實(shí)現(xiàn)Runnable接口的實(shí)現(xiàn)類(lèi)的實(shí)例對(duì)象作為T(mén)hread構(gòu)造函數(shù)的target
實(shí)現(xiàn)Callable接口通過(guò)FutureTask包裝器來(lái)創(chuàng)建Thread線程
通過(guò)線程池創(chuàng)建線程
前面兩種可以歸結(jié)為一類(lèi):無(wú)返回值,原因很簡(jiǎn)單,通過(guò)重寫(xiě)run方法,run方式的返回值是void,所以沒(méi)有辦法返回結(jié)果
后面兩種可以歸結(jié)成一類(lèi):有返回值,通過(guò)Callable接口,就要實(shí)現(xiàn)call方法,這個(gè)方法的返回值是Object,所以返回的結(jié)果可以放在Object對(duì)象中
線程實(shí)現(xiàn)方式3:通過(guò)Callable和FutureTask創(chuàng)建線程創(chuàng)建Callable接口的實(shí)現(xiàn)類(lèi) ,并實(shí)現(xiàn)Call方法
創(chuàng)建Callable實(shí)現(xiàn)類(lèi)的實(shí)現(xiàn),使用FutureTask類(lèi)包裝Callable對(duì)象,該FutureTask對(duì)象封裝了Callable對(duì)象的Call方法的返回值
使用FutureTask對(duì)象作為T(mén)hread對(duì)象的target創(chuàng)建并啟動(dòng)線程
調(diào)用FutureTask對(duì)象的get()來(lái)獲取子線程執(zhí)行結(jié)束的返回值
public class ThreadDemo03 { public static void main(String[] args) { // TODO Auto-generated method stub Callable
程序運(yùn)行結(jié)果:
main
Thread-0–>我是通過(guò)實(shí)現(xiàn)Callable接口通過(guò)FutureTask包裝器來(lái)實(shí)現(xiàn)的線程
public class ThreadDemo05{ private static int POOL_NUM = 10; //線程池?cái)?shù)量 public static void main(String[] args) throws InterruptedException { // TODO Auto-generated method stub ExecutorService executorService = Executors.newFixedThreadPool(5); for(int i = 0; i程序運(yùn)行結(jié)果:
通過(guò)線程池方式創(chuàng)建的線程:pool-1-thread-3 通過(guò)線程池方式創(chuàng)建的線程:pool-1-thread-4 通過(guò)線程池方式創(chuàng)建的線程:pool-1-thread-1 通過(guò)線程池方式創(chuàng)建的線程:pool-1-thread-5 通過(guò)線程池方式創(chuàng)建的線程:pool-1-thread-2 通過(guò)線程池方式創(chuàng)建的線程:pool-1-thread-5 通過(guò)線程池方式創(chuàng)建的線程:pool-1-thread-1 通過(guò)線程池方式創(chuàng)建的線程:pool-1-thread-4 通過(guò)線程池方式創(chuàng)建的線程:pool-1-thread-3 通過(guò)線程池方式創(chuàng)建的線程:pool-1-thread-2ExecutorService、Callable都是屬于Executor框架。返回結(jié)果的線程是在JDK1.5中引入的新特征,還有Future接口也是屬于這個(gè)框架,有了這種特征得到返回值就很方便了。
通過(guò)分析可以知道,他同樣也是實(shí)現(xiàn)了Callable接口,實(shí)現(xiàn)了Call方法,所以有返回值。這也就是正好符合了前面所說(shuō)的兩種分類(lèi)執(zhí)行Callable任務(wù)后,可以獲取一個(gè)Future的對(duì)象,在該對(duì)象上調(diào)用get就可以獲取到Callable任務(wù)返回的Object了。get方法是阻塞的,即:線程無(wú)返回結(jié)果,get方法會(huì)一直等待。
再介紹Executors類(lèi):newCachedThreadPool創(chuàng)建一個(gè)可緩存線程池,如果線程池長(zhǎng)度超過(guò)處理需要,可靈活回收空閑線程,若無(wú)可回收,則新建線程。
newFixedThreadPool 創(chuàng)建一個(gè)定長(zhǎng)線程池,可控制線程最大并發(fā)數(shù),超出的線程會(huì)在隊(duì)列中等待。
newScheduledThreadPool 創(chuàng)建一個(gè)定長(zhǎng)線程池,支持定時(shí)及周期性任務(wù)執(zhí)行。
newSingleThreadExecutor 創(chuàng)建一個(gè)單線程化的線程池,它只會(huì)用唯一的工作線程來(lái)執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級(jí))執(zhí)行。
Java多線程實(shí)現(xiàn)的四種方式
http://blog.csdn.net/u011480603/article/details/75332435
高并發(fā)、任務(wù)執(zhí)行時(shí)間短的業(yè)務(wù)怎樣使用線程池?并發(fā)不高、任務(wù)執(zhí)行時(shí)間長(zhǎng)的業(yè)務(wù)怎樣使用線程池?并發(fā)高、業(yè)務(wù)執(zhí)行時(shí)間長(zhǎng)的業(yè)務(wù)怎樣使用線程池?這是我在并發(fā)編程網(wǎng)上看到的一個(gè)問(wèn)題,把這個(gè)問(wèn)題放在最后一個(gè),希望每個(gè)人都能看到并且思考一下,因?yàn)檫@個(gè)問(wèn)題非常好、非常實(shí)際、非常專(zhuān)業(yè)。關(guān)于這個(gè)問(wèn)題,個(gè)人看法是:
(1)高并發(fā)、任務(wù)執(zhí)行時(shí)間短的業(yè)務(wù),線程池線程數(shù)可以設(shè)置為CPU核數(shù)+1,減少線程上下文的切換
(2)并發(fā)不高、任務(wù)執(zhí)行時(shí)間長(zhǎng)的業(yè)務(wù)要區(qū)分開(kāi)看:
a)假如是業(yè)務(wù)時(shí)間長(zhǎng)集中在IO操作上,也就是IO密集型的任務(wù),因?yàn)镮O操作并不占用CPU,所以不要讓所有的CPU閑下來(lái),可以加大線程池中的線程數(shù)目,讓CPU處理更多的業(yè)務(wù)
b)假如是業(yè)務(wù)時(shí)間長(zhǎng)集中在計(jì)算操作上,也就是計(jì)算密集型任務(wù),這個(gè)就沒(méi)辦法了,和(1)一樣吧,線程池中的線程數(shù)設(shè)置得少一些,減少線程上下文的切換
(3)并發(fā)高、業(yè)務(wù)執(zhí)行時(shí)間長(zhǎng),解決這種類(lèi)型任務(wù)的關(guān)鍵不在于線程池而在于整體架構(gòu)的設(shè)計(jì),看看這些業(yè)務(wù)里面某些數(shù)據(jù)是否能做緩存是第一步,增加服務(wù)器是第二步,至于線程池的設(shè)置,設(shè)置參考(2)。最后,業(yè)務(wù)執(zhí)行時(shí)間長(zhǎng)的問(wèn)題,也可能需要分析一下,看看能不能使用中間件對(duì)任務(wù)進(jìn)行拆分和解耦。
如果你提交任務(wù)時(shí),線程池隊(duì)列已滿,這時(shí)會(huì)發(fā)生什么?如果你使用的LinkedBlockingQueue,也就是無(wú)界隊(duì)列的話,沒(méi)關(guān)系,繼續(xù)添加任務(wù)到阻塞隊(duì)列中等待執(zhí)行,因?yàn)長(zhǎng)inkedBlockingQueue可以近乎認(rèn)為是一個(gè)無(wú)窮大的隊(duì)列,可以無(wú)限存放任務(wù);如果你使用的是有界隊(duì)列比方說(shuō)ArrayBlockingQueue的話,任務(wù)首先會(huì)被添加到ArrayBlockingQueue中,ArrayBlockingQueue滿了,則會(huì)使用拒絕策略RejectedExecutionHandler處理滿了的任務(wù),默認(rèn)是AbortPolicy。
鎖的等級(jí):方法鎖、對(duì)象鎖、類(lèi)鎖?方法鎖(synchronized修飾方法時(shí))
通過(guò)在方法聲明中加入 synchronized關(guān)鍵字來(lái)聲明 synchronized 方法。
synchronized 方法控制對(duì)類(lèi)成員變量的訪問(wèn):
每個(gè)類(lèi)實(shí)例對(duì)應(yīng)一把鎖,每個(gè) synchronized 方法都必須獲得調(diào)用該方法的類(lèi)實(shí)例的鎖方能執(zhí)行,否則所屬線程阻塞,方法一旦執(zhí)行,就獨(dú)占該鎖,直到從該方法返回時(shí)才將鎖釋放,此后被阻塞的線程方能獲得該鎖,重新進(jìn)入可執(zhí)行狀態(tài)。這種機(jī)制確保了同一時(shí)刻對(duì)于每一個(gè)類(lèi)實(shí)例,其所有聲明為 synchronized 的成員函數(shù)中至多只有一個(gè)處于可執(zhí)行狀態(tài),從而有效避免了類(lèi)成員變量的訪問(wèn)沖突。對(duì)象鎖(synchronized修飾方法或代碼塊)
當(dāng)一個(gè)對(duì)象中有synchronized method或synchronized block的時(shí)候調(diào)用此對(duì)象的同步方法或進(jìn)入其同步區(qū)域時(shí),就必須先獲得對(duì)象鎖。如果此對(duì)象的對(duì)象鎖已被其他調(diào)用者占用,則需要等待此鎖被釋放。(方法鎖也是對(duì)象鎖)
java的所有對(duì)象都含有1個(gè)互斥鎖,這個(gè)鎖由JVM自動(dòng)獲取和釋放。線程進(jìn)入synchronized方法的時(shí)候獲取該對(duì)象的鎖,當(dāng)然如果已經(jīng)有線程獲取了這個(gè)對(duì)象的鎖,那么當(dāng)前線程會(huì)等待;synchronized方法正常返回或者拋異常而終止,JVM會(huì)自動(dòng)釋放對(duì)象鎖。這里也體現(xiàn)了用synchronized來(lái)加鎖的1個(gè)好處,方法拋異常的時(shí)候,鎖仍然可以由JVM來(lái)自動(dòng)釋放?!?/p>
類(lèi)鎖(synchronized 修飾靜態(tài)的方法或代碼塊)
由于一個(gè)class不論被實(shí)例化多少次,其中的靜態(tài)方法和靜態(tài)變量在內(nèi)存中都只有一份。所以,一旦一個(gè)靜態(tài)的方法被申明為synchronized。此類(lèi)所有的實(shí)例化對(duì)象在調(diào)用此方法,共用同一把鎖,我們稱(chēng)之為類(lèi)鎖。
對(duì)象鎖是用來(lái)控制實(shí)例方法之間的同步,類(lèi)鎖是用來(lái)控制靜態(tài)方法(或靜態(tài)變量互斥體)之間的同步
如果同步塊內(nèi)的線程拋出異常會(huì)發(fā)生什么?這個(gè)問(wèn)題坑了很多Java程序員,若你能想到鎖是否釋放這條線索來(lái)回答還有點(diǎn)希望答對(duì)。無(wú)論你的同步塊是正常還是異常退出的,里面的線程都會(huì)釋放鎖,所以對(duì)比鎖接口我更喜歡同步塊,因?yàn)樗挥梦一ㄙM(fèi)精力去釋放鎖,該功能可以在finally block里釋放鎖實(shí)現(xiàn)。
并發(fā)編程(concurrency)并行編程(parallellism)有什么區(qū)別?并發(fā)(concurrency)和并行(parallellism)是:
解釋一:并行是指兩個(gè)或者多個(gè)事件在同一時(shí)刻發(fā)生;而并發(fā)是指兩個(gè)或多個(gè)事件在同一時(shí)間間隔發(fā)生。
解釋二:并行是在不同實(shí)體上的多個(gè)事件,并發(fā)是在同一實(shí)體上的多個(gè)事件。
解釋三:在一臺(tái)處理器上“同時(shí)”處理多個(gè)任務(wù),在多臺(tái)處理器上同時(shí)處理多個(gè)任務(wù)。如hadoop分布式集群
所以并發(fā)編程的目標(biāo)是充分的利用處理器的每一個(gè)核,以達(dá)到最高的處理性能。
如何保證多線程下 i++ 結(jié)果正確?根據(jù)volatile特性來(lái)用1000個(gè)線程不斷的累加數(shù)字,每次累加1個(gè),到最后值確不是1000.
volatile只能保證你數(shù)據(jù)的可見(jiàn)性(獲取到的是最新的數(shù)據(jù),不能保證原子性,說(shuō)白了,volatile跟原子性沒(méi)關(guān)系
import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; public class Counter { public static AtomicInteger count = new AtomicInteger();//原子操作 public static CountDownLatch latch= new CountDownLatch(1000);//線程協(xié)作處理 public static volatile int countNum = 0;//volatile 只能保證可見(jiàn)性,不能保證原子性 public static int synNum = 0;//同步處理計(jì)算 public static void inc() { try { Thread.sleep(1); } catch (InterruptedException e) { } countNum++; int c = count.addAndGet(1); add(); System.out.println(Thread.currentThread().getName() + "------>" + c); } public static synchronized void add(){ synNum++; } public static void main(String[] args) { //同時(shí)啟動(dòng)1000個(gè)線程,去進(jìn)行i++計(jì)算,看看實(shí)際結(jié)果 for (int i = 0; i < 1000; i++) { new Thread(new Runnable() { @Override public void run() { Counter.inc(); latch.countDown(); } },"thread" + i).start(); } try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()); System.out.println("運(yùn)行結(jié)果:Counter.count=" + count.get() + ",,," + countNum + ",,," + synNum); }count.get()是AtomicInteger的值;
count是用volatile修飾的變量的值;
synNum是用synchronized修飾的值;
用synchronized和AtomicInteger能保證是你想要的數(shù)據(jù),volatile并不能保證。
第一次運(yùn)行結(jié)果:
main
運(yùn)行結(jié)果:Counter.count=1000,,,991,,,1000第二次運(yùn)行結(jié)果:
main
運(yùn)行結(jié)果:Counter.count=1000,,,998,,,1000第三次運(yùn)行結(jié)果:
main
運(yùn)行結(jié)果:Counter.count=1000,,,993,,,1000可見(jiàn),就算用了volatile,也不能保證數(shù)據(jù)是你想要的數(shù)據(jù),volatile只能保證你數(shù)據(jù)的可見(jiàn)性(獲取到的是最新的數(shù)據(jù),不能保證原子性,說(shuō)白了,volatile跟原子性沒(méi)關(guān)系)
要保證原子性,對(duì)數(shù)據(jù)的累加,可以用AtomicInteger類(lèi);
也可以用synchronized來(lái)保證數(shù)據(jù)的一致性
一個(gè)線程如果出現(xiàn)了運(yùn)行時(shí)異常會(huì)怎么樣?如果這個(gè)異常沒(méi)有被捕獲的話,這個(gè)線程就停止執(zhí)行了。另外重要的一點(diǎn)是:如果這個(gè)線程持有某個(gè)某個(gè)對(duì)象的監(jiān)視器,那么這個(gè)對(duì)象監(jiān)視器會(huì)被立即釋放
如何在兩個(gè)線程之間共享數(shù)據(jù)?通過(guò)在線程之間共享對(duì)象就可以了,然后通過(guò)wait/notify/notifyAll、await/signal/signalAll進(jìn)行喚起和等待,比方說(shuō)阻塞隊(duì)列BlockingQueue就是為線程之間共享數(shù)據(jù)而設(shè)計(jì)的
生產(chǎn)者消費(fèi)者模型的作用是什么?這個(gè)問(wèn)題很理論,但是很重要:
(1)通過(guò)平衡生產(chǎn)者的生產(chǎn)能力和消費(fèi)者的消費(fèi)能力來(lái)提升整個(gè)系統(tǒng)的運(yùn)行效率,這是生產(chǎn)者消費(fèi)者模型最重要的作用
(2)解耦,這是生產(chǎn)者消費(fèi)者模型附帶的作用,解耦意味著生產(chǎn)者和消費(fèi)者之間的聯(lián)系少,聯(lián)系越少越可以獨(dú)自發(fā)展而不需要收到相互的制約
怎么喚醒一個(gè)阻塞的線程?如果線程是因?yàn)檎{(diào)用了wait()、sleep()或者join()方法而導(dǎo)致的阻塞,可以中斷線程,并且通過(guò)拋出InterruptedException來(lái)喚醒它;如果線程遇到了IO阻塞,無(wú)能為力,因?yàn)镮O是操作系統(tǒng)實(shí)現(xiàn)的,Java代碼并沒(méi)有辦法直接接觸到操作系統(tǒng)。
Java中用到的線程調(diào)度算法是什么?搶占式。一個(gè)線程用完CPU之后,操作系統(tǒng)會(huì)根據(jù)線程優(yōu)先級(jí)、線程饑餓情況等數(shù)據(jù)算出一個(gè)總的優(yōu)先級(jí)并分配下一個(gè)時(shí)間片給某個(gè)線程執(zhí)行。
單例模式的線程安全性?老生常談的問(wèn)題了,首先要說(shuō)的是單例模式的線程安全意味著:某個(gè)類(lèi)的實(shí)例在多線程環(huán)境下只會(huì)被創(chuàng)建一次出來(lái)。單例模式有很多種的寫(xiě)法,我總結(jié)一下:
(1)餓漢式單例模式的寫(xiě)法:線程安全
(2)懶漢式單例模式的寫(xiě)法:非線程安全
(3)雙檢鎖單例模式的寫(xiě)法:線程安全
線程類(lèi)的構(gòu)造方法、靜態(tài)塊是被哪個(gè)線程調(diào)用的?這是一個(gè)非常刁鉆和狡猾的問(wèn)題。請(qǐng)記住:線程類(lèi)的構(gòu)造方法、靜態(tài)塊是被new這個(gè)線程類(lèi)所在的線程所調(diào)用的,而run方法里面的代碼才是被線程自身所調(diào)用的。
如果說(shuō)上面的說(shuō)法讓你感到困惑,那么我舉個(gè)例子,假設(shè)Thread2中new了Thread1,main函數(shù)中new了Thread2,那么:
(1)Thread2的構(gòu)造方法、靜態(tài)塊是main線程調(diào)用的,Thread2的run()方法是Thread2自己調(diào)用的
(2)Thread1的構(gòu)造方法、靜態(tài)塊是Thread2調(diào)用的,Thread1的run()方法是Thread1自己調(diào)用的
同步方法和同步塊,哪個(gè)是更好的選擇?同步塊是更好的選擇,因?yàn)樗粫?huì)鎖住整個(gè)對(duì)象(當(dāng)然也可以讓它鎖住整個(gè)對(duì)象)。同步方法會(huì)鎖住整個(gè)對(duì)象,哪怕這個(gè)類(lèi)中有多個(gè)不相關(guān)聯(lián)的同步塊,這通常會(huì)導(dǎo)致他們停止執(zhí)行并需要等待獲得這個(gè)對(duì)象上的鎖。
public class SynObj{
public synchronized void showA(){ System.out.println("showA.."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } public void showB(){ synchronized (this) { System.out.println("showB.."); } }}
如何檢測(cè)死鎖?怎么預(yù)防死鎖?所謂死鎖:是指兩個(gè)或兩個(gè)以上的進(jìn)程在執(zhí)行過(guò)程中,因爭(zhēng)奪資源而造成的一種互相等待的現(xiàn)象,若無(wú)外力作用,它們都將無(wú)法推進(jìn)下去。此時(shí)稱(chēng)系統(tǒng)處于死鎖
通俗地講就是兩個(gè)或多個(gè)進(jìn)程被無(wú)限期地阻塞、相互等待的一種狀態(tài)
死鎖產(chǎn)生的原因?
1.因競(jìng)爭(zhēng)資源發(fā)生死鎖 現(xiàn)象:系統(tǒng)中供多個(gè)進(jìn)程共享的資源的數(shù)目不足以滿足全部進(jìn)程的需要時(shí),就會(huì)引起對(duì)諸資源的競(jìng)爭(zhēng)而發(fā)生死鎖現(xiàn)象
2.進(jìn)程推進(jìn)順序不當(dāng)發(fā)生死鎖
死鎖的四個(gè)必要條件:
互斥條件:進(jìn)程對(duì)所分配到的資源不允許其他進(jìn)程進(jìn)行訪問(wèn),若其他進(jìn)程訪問(wèn)該資源,只能等待,直至占有該資源的進(jìn)程使用完成后釋放該資源
請(qǐng)求和保持條件:進(jìn)程獲得一定的資源之后,又對(duì)其他資源發(fā)出請(qǐng)求,但是該資源可能被其他進(jìn)程占有,此事請(qǐng)求阻塞,但又對(duì)自己獲得的資源保持不放
不可剝奪條件:是指進(jìn)程已獲得的資源,在未完成使用之前,不可被剝奪,只能在使用完后自己釋放
環(huán)路等待條件:是指進(jìn)程發(fā)生死鎖后,若干進(jìn)程之間形成一種頭尾相接的循環(huán)等待資源關(guān)系
這四個(gè)條件是死鎖的必要條件,只要系統(tǒng)發(fā)生死鎖,這些條件必然成立,而只要上述條件之
一不滿足,就不會(huì)發(fā)生死鎖。檢測(cè)死鎖
有兩個(gè)容器,一個(gè)用于保存線程正在請(qǐng)求的鎖,一個(gè)用于保存線程已經(jīng)持有的鎖。每次加鎖之前都會(huì)做如下檢測(cè):
檢測(cè)當(dāng)前正在請(qǐng)求的鎖是否已經(jīng)被其它線程持有,如果有,則把那些線程找出來(lái)
遍歷第一步中返回的線程,檢查自己持有的鎖是否正被其中任何一個(gè)線程請(qǐng)求,如果第二步返回真,表示出現(xiàn)了死鎖
死鎖的解除與預(yù)防:
理解了死鎖的原因,尤其是產(chǎn)生死鎖的四個(gè)必要條件,就可以最大可能地避免、預(yù)防和
解除死鎖。所以,在系統(tǒng)設(shè)計(jì)、進(jìn)程調(diào)度等方面注意如何不讓這四個(gè)必要條件成立,如何確
定資源的合理分配算法,避免進(jìn)程永久占據(jù)系統(tǒng)資源。此外,也要防止進(jìn)程在處于等待狀態(tài)的情況下占用資源。因此,對(duì)資源的分配要給予合理的規(guī)劃。
http://blog.csdn.net/yyf_it/a...
推薦閱讀
http://blog.csdn.net/yyf_it/article/details/52412071想進(jìn)大廠?50個(gè)多線程面試題,你會(huì)多少?(一)
想進(jìn)大廠?50個(gè)多線程面試題,你會(huì)多少?(二)
BTA 常問(wèn)的 Java基礎(chǔ)40道常見(jiàn)面試題及詳細(xì)答案
Spring 常見(jiàn)的一些面試題整理
常用的分布式事務(wù)解決方案介紹有多少種?
什么是微服務(wù)架構(gòu)?
Dapper,大規(guī)模分布式系統(tǒng)的跟蹤系統(tǒng)
Contact作者:鵬磊
出處:http://www.ymq.io
版權(quán)歸作者所有,轉(zhuǎn)載請(qǐng)注明出處
Wechat:關(guān)注公眾號(hào),搜云庫(kù),專(zhuān)注于開(kāi)發(fā)技術(shù)的研究與知識(shí)分享
關(guān)注微信公眾號(hào) "搜云庫(kù)" 獲取最新文章 【福利】公眾號(hào)后臺(tái)回復(fù) “進(jìn)群” 拉你進(jìn)微信【技術(shù)分享群】 【福利】在里面你可以認(rèn)識(shí)到很多搞技術(shù)大佬,免費(fèi)提問(wèn),互相學(xué)習(xí)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/68879.html
摘要:為程序員金三銀四精心挑選的余道面試題與答案,歡迎大家向我推薦你在面試過(guò)程中遇到的問(wèn)題我會(huì)把大家推薦的問(wèn)題添加到下面的常用面試題清單中供大家參考。 為Java程序員金三銀四精心挑選的300余道Java面試題與答案,歡迎大家向我推薦你在面試過(guò)程中遇到的問(wèn)題,我會(huì)把大家推薦的問(wèn)題添加到下面的常用面試題清單中供大家參考。 前兩天寫(xiě)的以下博客,大家比較認(rèn)可,熱度不錯(cuò),希望可以幫到準(zhǔn)備或者正在參加...
摘要:前言從號(hào)開(kāi)始在寫(xiě)下第一篇文章說(shuō)是筆記還差不多,驚奇地收到有人收藏我的文章的消息,覺(jué)得有點(diǎn)開(kāi)心。突然腦子抽到想爬下里標(biāo)簽下的文章有多少,哪篇被收藏最多,哪篇被點(diǎn)贊最多。。?,F(xiàn)在和大家分享下,收藏量前的文章,被那么多人收藏應(yīng)該是篇值得看的文章。 前言 從18號(hào)開(kāi)始在sf寫(xiě)下第一篇文章(說(shuō)是筆記還差不多),驚奇地收到有人收藏我的文章的消息,覺(jué)得有點(diǎn)開(kāi)心。突然腦子抽到想爬下sf里JAVA標(biāo)簽下...
摘要:下面是線程相關(guān)的熱門(mén)面試題,你可以用它來(lái)好好準(zhǔn)備面試。線程安全問(wèn)題都是由全局變量及靜態(tài)變量引起的。持有自旋鎖的線程在之前應(yīng)該釋放自旋鎖以便其它線程可以獲得自旋鎖。 最近看到網(wǎng)上流傳著,各種面試經(jīng)驗(yàn)及面試題,往往都是一大堆技術(shù)題目貼上去,而沒(méi)有答案。 不管你是新程序員還是老手,你一定在面試中遇到過(guò)有關(guān)線程的問(wèn)題。Java語(yǔ)言一個(gè)重要的特點(diǎn)就是內(nèi)置了對(duì)并發(fā)的支持,讓Java大受企業(yè)和程序員...
閱讀 3004·2021-10-13 09:39
閱讀 2700·2021-09-27 13:34
閱讀 2040·2019-08-30 15:55
閱讀 3267·2019-08-30 15:43
閱讀 3646·2019-08-30 11:16
閱讀 1762·2019-08-26 18:28
閱讀 1297·2019-08-26 13:56
閱讀 923·2019-08-26 13:35