注 : JAVA中的堆棧和內存模型:,java
Java內存模型是圍繞着在併發過程當中如何處理原子性、可見性和有序性來創建的(三個特性詳解見10.)
a.Java內存模型將內存分爲了主內存和工做內存
b.Java內存模型規定全部的變量都存儲在主內存中,每一個線程有本身的工做內存
c.主內存主要包括:堆和方法區,主內存是全部線程共享的
d.工做內存主要包括:該線程私有的棧和對主內存部分變量拷貝的寄存器(包括程序計數器和cpu高速緩存區)
e.Java內存模型規定了全部變量都存儲在主內存中,每一個線程有本身的工做內存,線程的工做內存中保存了該線程使用到的變量到主內存副本拷貝,線程對變量的全部操做都必須在本身的工做內存中進行,而不能直接讀寫主內存中的變量,不一樣線程之間也沒法直接操做對方工做內存中的變量,線程間變量值的傳遞須要經過主內存來完成,面試
①當變量被volatile關鍵字修飾時,對於共享資源的讀操做會直接在主內存中進行(固然也會緩存到工做內存中,當其餘線程對該共享資源進行了修改,則會致使當前線程在工做內存中的共享資源失效,因此必須從主內存中再次獲取),對於共享資源的寫操做固然是先要修改工做內存,可是修改結束後會馬上將其刷新到主內存中。數組
②經過synchronized關鍵字可以保證可見性,synchronized關鍵字可以保證同一時刻只有一個線程得到鎖,而後執行同步方法,而且還會確保在鎖釋放以前,會將對變量的修改刷新到主內存當中。JVM規範定義了線程對內存間交互的八種操做:(待補充)緩存
1.棧空間(stack),連續的存儲空間,遵循後進先出的原則,存放基本類型的變量數據和對象的引用,但對象自己不存放在棧中,而是存放在堆(new 出來的對象)或者常量池中(字符串常量對象存放在常量池中。); 當在一段代碼塊定義一個變量時,Java在棧中爲這個變量分配內存空間,當該變量退出其做用域Ⅰ後,Java會自動釋放掉爲該變量所分配的內存空間,該內存空間能夠當即被另做他用。
注:Ⅰ:變量的做用域:從變量定義的位置開始,到該變量所在的那對大括號結束
Ⅱ:變量週期性: 從變量定義的位置開始就在內存中活了;到達它所在的做用域的時候就在內存中消失了;多線程
2.堆空間(heap),不連續的空間,用於存放new出的對象,或者說是類的實例;當引用變量是普通的變量,定義時在棧中分配,引用變量在程序運行到其做用域以外後被釋放。而數組和對象自己在堆中分配,即便程序運行到使用 new 產生數組或者對象的語句所在的代碼塊以外,數組和對象自己佔據的內存不會被釋放,數組和對象在沒有引用變量指向它的時候,才變爲垃圾,不能在被使用,但仍然佔據內存空間不放,在隨後的一個不肯定的時間被垃圾回收器收走(釋放掉)。這也是 Java 比較佔內存的緣由。
實際上,棧中的變量指向堆內存中的變量,這就是Java中的指針!併發
3.堆與棧:堆是由垃圾回收來負責的,堆的優點是能夠動態地分配內存 大小,生存期也沒必要事先告訴編譯器,由於它是在運行時動態分配內存的,Java的垃圾收集器會自動收走這些再也不使用的數據。但缺點是,因爲要在運行時動態 分配內存,存取速度較慢。app
棧的優點是,存取速度比堆要快,僅次於寄存器,棧數據能夠共享(int a = 3再int b = 3此時內存中值存在一個3,a,b兩個引用同時指向同一個3)。但缺點是,存在棧中的數據大小與生存期必須是肯定的,缺少靈活性。對於棧和常量池中的對象Ⅰ能夠共享,對於堆中的對象不能夠共享。棧中的數據大小和生命週期是能夠肯定的。堆中的對象的由垃圾回收器負責回收,所以大小和生命週期不須要肯定 ,具備很大的靈活性。
注:Ⅰ:用new來生成的對象都是放在堆中的,直接定義的局部變量都是放在棧中的,全局和靜態的對象是放在數據段的靜態存儲區,例如: Class People;People p;//棧上分配內存People* pPeople;pPeople = new People;//堆上分配內存
對於字符串:其對象的引用都是存儲在棧中的,若是是 編譯期已經建立好(直接用雙引號定義的)的就存儲在常量池中,若是是運行期(new出來的)才肯定的就存儲在堆中 。對於equals相等的字符串,在常量池中永遠只有一份,在堆中有多份。異步
4.方法區(method),方法區在堆空間內,用於存放 ①類的代碼信息;②靜態變量和方法;③常量池(字符串常量和基本類型常量(public static final),具備共享機制);常量池指的是在編譯期被肯定,並被保存在已編譯的.class文件中的一些數據。除了包含代碼中所定義的各類基本類型(如int、long等等)和對象型(如String及數組)的常量值(final)還包含一些以文本形式出現的符號引用,好比:類和接口的全限定名;字段的名稱和描述符;方法和名稱和描述符。
Java中除了基本數據類型,其餘的均是引用類型,包括類、數組等等。ide
2.1.進程和線程的區別是什麼?
線程是操做系統可以進行運算調度的最小單位也是進程中的實際運做單位。一個進程能夠有不少線程,每條線程並行執行不一樣的任務。不一樣的進程使用不一樣的內存空間,而當前進程下的全部線程共享一片相同的內存空間。 每一個線程都擁有單獨的棧內存用來存儲本地數據 .性能
線程在執行過程當中,能夠處於下面幾種狀態:
就緒(Runnable):線程準備運行,不必定立馬就能開始執行。
運行中(Running):進程正在執行線程的代碼。
等待中(Waiting):線程處於阻塞的狀態,等待外部的處理結束。
睡眠中(Sleeping):線程被強制睡眠。
I/O 阻塞(Blocked on I/O):等待 I/O 操做完成。
同步阻塞(Blocked on Synchronization):等待獲取鎖。
死亡(Dead):線程完成了執行。
有三種方式能夠用來建立線程:
public class ThreadTest extends Thread { private int ticket = 10; @Override public void run() { for (int i = 0; i < 10; i++) { synchronized (this) { if (this.ticket > 0) { try { Thread.sleep(100); System.out.println(Thread.currentThread().getName() + "賣票---->" + (this.ticket--)); } catch (InterruptedException e) { e.printStackTrace(); } } } } } public static void main(String[] arg) { ThreadTest t1 = new ThreadTest(); new Thread(t1, "線程1").start(); new Thread(t1, "線程2").start(); //也達到了資源共享的目的然而事實卻不盡如此。 } }
public class RunnableTest implements Runnable { private int ticket = 10; @Override public void run() { for (int i = 0; i < 10; i++) { //添加同步快 synchronized (this) { if (this.ticket > 0) { try { //經過睡眠線程來模擬出最後一張票的搶票場景 Thread.sleep(100); System.out.println(Thread.currentThread().getName() + "賣票---->" + (this.ticket--)); } catch (Exception e) { e.printStackTrace(); } } } } } public static void main(String[] arg) { RunnableTest t1 = new RunnableTest(); new Thread(t1, "線程1").start(); new Thread(t1, "線程2").start(); } }
MyTask.java類
FutureTask使用方法:
1.Callable規定的方法是call(),而Runnable規定的方法是run().
2.Callable的任務執行後可返回值,而Runnable的任務是不能返回值的。
3.call() 方法可拋出異常,而run() 方法是不能拋出異常的。
運行Callable任務可拿到一個FutureTask對象, FutureTask表示異步計算的結果
線程池就是首先建立一些線程,它們的集合稱爲線程池。使用線程池能夠很好地提升性能,線程池在系統啓動時即建立大量空閒的線程,程序將一個任務傳給線程池,線程池就會啓動一條線程來執行這個任務,執行結束之後,該線程並不會死亡,而是再次返回線程池中成爲空閒狀態,等待執行下一個任務。
在線程池的工做模式下,任務是整個提交給線程池的,而不是直接提交給某個線程,線程池在拿到任務後,就在內部尋找是否有空閒的線程,若是有,則將任務交給某個空閒的線程。一個線程同時只能執行一個任務,但能夠同時向一個線程池提交多個任務。
下降資源的消耗
經過重複利用已經建立好的線程下降線程建立和銷燬帶來的損耗
提升響應速度
線程池中的線程沒有超過上限時,有線程處於等待分配任務的狀態,當任務來時無需建立線程這一步驟就能直接執行。
提升線程的可管理性
線程池裏提供了操做線程的方法,這就爲對管理線程提供了可能性。
線程池的返回值ExecutorService: 是Java提供的用於管理線程池的類。該類的兩個做用:控制線程數量和重用線程
咱們在實際業務中,以上三種線程啓動的方式都不用。 將全部的多線程異步任務都交給線程池
建立
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor();
原生線程池的七大參數
corePoolSize : 核心線程數(一致存在除非設置了allowThreadTimeOut),線程池建立好以後就等待來接受異步任務去執行。
maximumPoolSize : 最大線程數,控制資源併發
keepAliveTime : 存活時間,若是當前線程數量大於核心線程數,且線程空閒的時間大於指定的keepAliveTime就會釋放線程(不會釋放核心線程)
unit : 指定存活時間的時間單位
BlockingQueue workQueue : 阻塞隊列的最大數量(該值的大小有壓力測試後的峯值決定),若是任務數大於maximumPoolSize,就會將任務放在隊列裏,只要有線程空閒就會去隊列裏去除新的任務執行。
ThreadFactory threadFactory : 線程的建立工廠。
RejectedExecutionHandler handler : 若是workQueue滿了按照指定的拒絕策略拒絕執行任務
運行流程
1.線程池建立,準備好core數量的核心線程,準備接受任務。
2.新的任務進來用core準備好的空閒線程執行。
(1)若是core滿了,就將再進來的任務放入阻塞隊列中,空閒的core就會本身去阻塞隊列獲取任務執行
(2)若是阻塞隊列滿了,就直接開新線程執行,最大隻能開到max指定的數量
(3)max任務都執行好了。Max減去core的數量的空閒線程會在keepAliveTime 指定的時間後自動銷燬。最終保持到core大小
(4)若是線程數開到max的數量還不夠用就是用RejectedExecutionHandler 指定的拒絕策略進行處理。
3.全部的線程都是由指定的factory建立
面試提
3.4.2.1 Executors.newCacheThreadPool():
可緩存線程池,core的數量爲0,全部均可以回收, 先查看池中有沒有之前創建的線程,若是有,就直接使用。若是沒有,就建一個新的線程加入池中,緩存型池子一般用於執行一些生存期很短的異步型任務(運行結果見下彙總圖).
線程池爲無限大,當執行當前任務時上一個任務已經完成,會複用執行上一個任務的線程,而不用每次新建線程
建立一個可重用固定個數的線程池,core的數量爲max,都不能夠回收,以共享的無界隊列方式來運行這些線程。(運行結果見下彙總圖).
建立一個單線程化的線程池,從阻塞隊列裏挨個獲取任務,它只會用惟一的工做線程來執行任務,保證全部任務按照指定順序(FIFO , LIFO,優先級)執行(運行結果見下彙總圖).
以上的全部execute均可以使用submit代替,而且submit能夠有返回值
CompletableFuture提供了四個靜態方法來建立一個異步操做。
Supplier supplier : 參數爲一個方法
Executor executor能夠傳入自定義線程池,不然使用本身默認的線程池
public static CompletableFuture<Void> runAsync(Runnable runnable) public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
給出一個例子
public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService future= Executors.newFixedThreadPool(10); CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> { System.out.println("當前線程------------------" + Thread.currentThread().getName()); int i = 10 / 2; return i; }, future); //獲取異步執行的結果在線程執任務行完以後返回 Integer integer = integerCompletableFuture.get(); System.out.println("結果爲"+integer);//結果爲5 }
CompletableFuture提供了四個方法計算完成時回調方法
public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action) public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action) public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor) public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)
下面給出一個例子
//方法執行完成後的感知 public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService future= Executors.newFixedThreadPool(10); CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> { System.out.println("當前線程------------------" + Thread.currentThread().getName()); int i = 10 / 0;//使用int i = 10 / 0;模擬有異常 return i; }, future).whenComplete((ems,exception)->{ //在出現異常時雖然能夠感知異常但不能修改數據 System.out.println("計算結果爲:"+ems+"----異常爲"+exception); }).exceptionally((throwable)->{ //能夠感知異常,並能夠返回結果 return 10; }); Integer integer = integerCompletableFuture.get(); System.out.println(integer); }
whenComplete能夠感知正常和異常的計算結果,無異常時直接返回結果,在感知到異常時使用exceptionally處理異常狀況
whenComplete和whenCompleteAsync的區別:
whenComplete : 是執行當前任務的線程執行繼續執行whenComplete的任務
whenCompleteAsync :是執行把whenCompleteAsync這個任務繼續提交給線程池來執行
方法不以Async結尾,意味着Action使用相同的線程執行,而Async可能會使用其餘線程執行(若是是使用相同的線程池也可能會被同一個線程選中執行)
public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn) public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn) public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor)
下面給出一個例子
//方法完成後的處理 public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService future= Executors.newFixedThreadPool(10); CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> { System.out.println("當前線程------------------" + Thread.currentThread().getName()); int i = 10 / 2;//使用int i = 10 / 0;模擬有異常 return i; }, future).handle((result,throwable)->{ if(result!=null){ return result*2; } if(throwable!=null){ return result*0; } return 0; }); Integer integer = integerCompletableFuture.get(); System.out.println(integer); }
和whenComplete同樣,能夠對結果作最後的處理(可處理異常),可改變返回值。
public CompletableFuture<Void> thenRun(Runnable action) public CompletableFuture<Void> thenRunAsync(Runnable action) public CompletableFuture<Void> thenRunAsync(Runnable action,Executor executor) public CompletableFuture<Void> thenAccept(Consumer<? super T> action) public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor) public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn) public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
thenRun:處理完任務後執行thenRun後面的方法
thenAccept:消費處理結果。接受任務的執行結果並消費處理,無返回結果
thenApply:當一個線程依賴另外一個線程時,獲取上一個任務返回的結果,並返回當前任務的返回值
以上全部都要前置任務完成
給出一個例子
public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService future= Executors.newFixedThreadPool(10); CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> { System.out.println("當前線程------------------" + Thread.currentThread().getName()); int i = 10 / 2; return i; }, future).thenApplyAsync(res -> { return res+3; }, future); Integer integer = integerCompletableFuture.get();//此處結果爲8 System.out.println(integer); }
thenRun不能獲取到上一步執行結果
thenAccept:能接受上一步執行結果但沒返回值
thenApply:既能接受上一步執行結果也有返回值
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,Runnable action) public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action) public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action,Executor executor) public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action) public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action) public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action, Executor executor) public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn) public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn) public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn, Executor executor)
給出一個例子
public CompletableFuture<Void> runAfterEither(CompletionStage<?> other,Runnable action) public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action) public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action,Executor executor) public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action) public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action) public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action,Executor executor) public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn) public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn) public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn,Executor executor)
給出一個例子
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
allOf : 阻塞線程,等待全部任務完成,才繼續往下進行不然阻塞
anyOf : 阻塞線程,只要有一個任務完成,就繼續往下進行
給出一個例子
Java 語言中,每一個對象有一把鎖。線程可使用synchronized關鍵字獲取對象上的鎖。
同步代碼塊:
synchronized(鎖對象){ 須要同步的代碼 }
此處的所對象必須是存在堆中(多個線程的共享資源)的對象
同步方法:
權限關鍵字 synchronized 返回值 方法名(){ 須要被同步的代碼塊 }
同步方法的鎖對象是this
靜態方法及鎖對象問題: 鎖對象是類的字節碼文件對象(類的class文件)
① 當前線程的同步方法、代碼塊執行結束的時候釋放
② 當前線程在同步方法、同步代碼塊中遇到break、return 終於該代碼塊或者方法的時候釋放。
③ 當前線程出現未處理的error或者exception致使異常結束的時候釋放。
④ 程序執行了同步對象wait方法,當前線程暫停,釋放鎖。
兩大鎖的根接口,Lock表明實現類是ReentrantLock(可重入鎖),ReadWriteLock(讀寫鎖)的表明實現類是ReentrantReadWriteLock。
①synchronized實現同步線程(IO讀文件時)阻塞不釋放鎖時,其餘線程需一直等待,Lock能夠經過只等待必定的時間 (tryLock(long time, TimeUnit unit)) 或者可以響應中斷(lockInterruptibly())解決。
②多個線程讀寫文件時,讀1操做與讀2操做不會起衝突synchronized實現的同步的話也只有一個線程在執行讀1操做.讀2操做需等待,Lock能夠解決這種狀況 (ReentrantReadWriteLock)。
③能夠經過Lock得知線程有沒有成功獲取到鎖 (解決方案:ReentrantLock) ,但這個是synchronized沒法辦到的。
lock();用來獲取鎖。若是鎖已被其餘線程獲取,則進行等待;必須在try…catch…塊中進行,而且將釋放鎖的操做放在finally塊中進行,
*tryLock()😗嘗試獲取鎖,獲取成功返回true;獲取失敗(鎖已被其餘線程獲取),返回false,這個方法不管如何都會當即返回(在拿不到鎖時不會一直在那等待)
*tryLock(long time, TimeUnit unit)😗拿不到鎖時會等待必定的時間,在時間期限以內若是還拿不到鎖,就返回false,同時能夠響應中斷。拿到鎖,則返回true。
*lockInterruptibly()😗當經過這個方法去獲取鎖時,若是其餘線程正在等待獲取鎖,則這個線程可以響應中斷,即中斷線程的等待狀態。也就使說,當兩個線程同時經過lock.lockInterruptibly()想獲取某個鎖時,倘若此時線程A獲取到了鎖,而線程B只有等待,那麼對線程B調用threadB.interrupt()方法可以中斷線程B的等待過程。
interrupt()方法只能中斷阻塞過程當中的線程而不能中斷正在運行過程當中的線程。
unlock:釋放鎖在finally語句塊中執行。
維護了一對相關的鎖,一個用於只讀操做,另外一個用於寫入操做。只要沒有 writer,讀取鎖能夠由多個 reader 線程同時保持,而寫入鎖是獨佔的。
rl.readLock();//返回Lock接口可經過Lock接口內方法獲取鎖
rl.writeLock();//返回Lock接口可經過Lock接口內方法獲取鎖
volatile用以聲明變量的值可能隨時會別的線程修改,使用volatile修飾的變量會強制將修改的值當即寫入主存,主存中值的更新會使緩存中的值失效(非volatile變量不具有這樣的特性,非volatile變量的值會被緩存,線程A更新了這個值,線程B讀取這個變量的值時可能讀到的並非是線程A更新後的值)。volatile會禁止指令重排。
注意,volatile不具有原子性,這是volatile與java中的synchronized、java.util.concurrent.locks.Lock最大的功能差別,這一點在面試中也是很是容易問到的點。
原子性:原子性一般指多個操做不存在只執行一部分的狀況,要麼所有執行要麼所有失敗
可見性:當多個線程訪問同一個變量x時,線程1修改了變量x的值,線程一、線程2…線程n可以當即讀取到線程1修改後的值。
有序性:即程序執行時按照代碼書寫的前後順序執行。在Java內存模型中,容許編譯器和處理器對指令進行重排序,可是重排序過程不會影響到單線程程序的執行,卻會影響到多線程併發執行的正確性。
那麼可能的一個執行順序是:語句2 -> 語句1 -> 語句3 -> 語句4
那麼可不多是這個執行順序: 語句2 -> 語句1 -> 語句4 -> 語句3。
不可能,由於處理器在進行重排序時是會考慮指令之間的數據依賴性,若是一個指令Instruction 2必須用到Instruction 1的結果,那麼處理器會保證Instruction 1會在Instruction 2以前執行。重排序不會影響單個線程內程序執行的結果,但在多線程處理器不能保證