Java併發總結

Java併發

進程

進程是程序的一次執行過程,是系統運行程序的基本單位,所以進程是動態的。系統運行一個程序便是一個進程從建立,運行到消亡的過程。java

在 Java 中,當咱們啓動 main 函數時其實就是啓動了一個 JVM 的進程,而 main 函數所在的線程就是這個進程中的一個線程,也稱主線程。緩存

線程

線程與進程類似,但線程是一個比進程更小的執行單位。一個進程在其執行的過程當中能夠產生多個線程。與進程不一樣的是同類的多個線程共享進程的堆和方法區資源,但每一個線程有本身的程序計數器、虛擬機棧和本地方法棧,因此係統在產生一個線程,或是在各個線程之間做切換工做時,負擔要比進程小得多,也正由於如此,線程也被稱爲輕量級進程。多線程

一個進程中能夠有多個線程,多個線程共享進程的堆和方法區 (JDK1.8 以後的元空間)資源,可是每一個線程有本身的程序計數器、虛擬機棧 和 本地方法棧。併發

總結: 線程 是 進程 劃分紅的更小的運行單位。線程和進程最大的不一樣在於基本上各進程是獨立的,而各線程則不必定,由於同一進程中的線程極有可能會相互影響。線程執行開銷小,但不利於資源的管理和保護;而進程正相反函數

Java線程生命週期和狀態

線程死鎖

多個線程同時被阻塞,它們中的一個或者所有都在等待某個資源被釋放。因爲線程被無限期地阻塞,所以程序不可能正常終止。工具

public class DeadLockDemo {
    private static Object resource1 = new Object();//資源 1
    private static Object resource2 = new Object();//資源 2

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (resource1) {
                System.out.println(Thread.currentThread() + "get resource1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread() + "waiting get resource2");
                synchronized (resource2) {
                    System.out.println(Thread.currentThread() + "get resource2");
                }
            }
        }, "線程 1").start();

        new Thread(() -> {
            synchronized (resource2) {
                System.out.println(Thread.currentThread() + "get resource2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread() + "waiting get resource1");
                synchronized (resource1) {
                    System.out.println(Thread.currentThread() + "get resource1");
                }
            }
        }, "線程 2").start();
    }
}

避免線程死鎖

咱們只要破壞產生死鎖的四個條件中的其中一個就能夠了。線程

  • 破壞互斥條件
    這個條件咱們沒有辦法破壞,由於咱們用鎖原本就是想讓他們互斥的(臨界資源須要互斥訪問)。
  • 破壞請求與保持條件
    一次性申請全部的資源。
  • 破壞不剝奪條件
    佔用部分資源的線程進一步申請其餘資源時,若是申請不到,能夠主動釋放它佔有的資源。
  • 破壞循環等待條件
    靠按序申請資源來預防。按某一順序申請資源,釋放資源則反序釋放。破壞循環等待條件。

sleep() 方法和 wait() 方法區別和共同點

  • 二者最主要的區別在於:sleep 方法沒有釋放鎖,而 wait 方法釋放了鎖 。
  • 二者均可以暫停線程的執行。
  • Wait 一般被用於線程間交互/通訊,sleep 一般被用於暫停執行。
  • wait() 方法被調用後,線程不會自動甦醒,須要別的線程調用同一個對象上的 notify() 或者 notifyAll() 方法。sleep() 方法執行完成後,線程會自動甦醒。或者可使用wait(long timeout)超時後線程會自動甦醒。

調用 start 方法方可啓動線程並使線程進入就緒狀態,而 run 方法只是 thread 的一個普通方法調用,仍是在主線程裏執行。code

synchronized關鍵字最主要的三種使用方式:

  • 修飾實例方法: 做用於當前對象實例加鎖,進入同步代碼前要得到當前對象實例的鎖
  • 修飾靜態方法: :也就是給當前類加鎖,會做用於類的全部對象實例,由於靜態成員不屬於任何一個實例對象,是類成員( static 代表這是該類的一個靜態資源,無論new了多少個對象,只有一份)。因此若是一個線程A調用一個實例對象的非靜態 synchronized 方法,而線程B須要調用這個實例對象所屬類的靜態 synchronized 方法,是容許的,不會發生互斥現象,由於訪問靜態 synchronized 方法佔用的鎖是當前類的鎖,而訪問非靜態 synchronized 方法佔用的鎖是當前實例對象鎖。
  • 修飾代碼塊: 指定加鎖對象,對給定對象加鎖,進入同步代碼庫前要得到給定對象的鎖。

總結: synchronized 關鍵字加到 static 靜態方法和 synchronized(class)代碼塊上都是是給 Class 類上鎖。synchronized 關鍵字加到實例方法上是給對象實例上鎖。儘可能不要使用 synchronized(String a) 由於JVM中,字符串常量池具備緩存功能!對象

線程池

線程池提供了一種限制和管理資源(包括執行一個任務)。 每一個線程池還維護一些基本統計信息,例如已完成任務的數量。blog

使用線程池的好處:

  • 下降資源消耗。 經過重複利用已建立的線程下降線程建立和銷燬形成的消耗。
  • 提升響應速度。 當任務到達時,任務能夠不須要的等到線程建立就能當即執行。
  • 提升線程的可管理性。 線程是稀缺資源,若是無限制的建立,不只會消耗系統資源,還會下降系統的穩定性,使用線程池能夠進行統一的分配,調優和監控。

實現Runnable接口和Callable接口的區別

若是想讓線程池執行任務的話須要實現的Runnable接口或Callable接口。 Runnable接口或Callable接口實現類均可以被ThreadPoolExecutor或 ScheduledThreadPoolExecutor執行。二者的區別在於 Runnable 接口不會返回結果可是 Callable 接口能夠返回結果。

備註: 工具類Executors能夠實現Runnable對象和Callable對象之間的相互轉換。(Executors.callable(Runnable task)或Executors.callable(Runnable task,Object resule))。

執行execute()方法和submit()方法的區別

  1. execute() 方法用於提交不須要返回值的任務,因此沒法判斷任務是否被線程池執行成功與否;

  2. submit() 方法用於提交須要返回值的任務。線程池會返回一個Future類型的對象,經過這個Future對象能夠判斷任務是否執行成功,而且能夠經過future的get()方法來獲取返回值,get()方法會阻塞當前線程直到任務完成,而使用 get(long timeout,TimeUnit unit)方法則會阻塞當前線程一段時間後當即返回,這時候有可能任務沒有執行完。

生產者/消費者模式

生產者和消費者問題是線程模型中的經典問題:生產者和消費者在同一時間段內共用同一個存儲空間,生產者往存儲空間中添加產品,消費者從存儲空間中取走產品,當存儲空間爲空時,消費者阻塞,當存儲空間滿時,生產者阻塞。

生產者消費者問題是研究多線程程序時繞不開的經典問題之一,它描述是有一塊緩衝區做爲倉庫,生產者能夠將產品放入倉庫,消費者則能夠從倉庫中取走產品。在Java中一共有四種方法支持同步,其中前三個是同步方法,一個是管道方法。

  1. Object的wait() / notify()方法
  2. Lock和Condition的await() / signal()方法
  3. BlockingQueue阻塞隊列方法
  4. PipedInputStream / PipedOutputStream

countDownLatch(倒計時門栓)

  • countDownLatch這個類使一個線程等待其餘線程各自執行完畢後再執行。
  • 是經過一個計數器來實現的,計數器的初始值是線程的數量。每當一個線程執行完畢後,計數器的值就-1,當計數器的值爲0時,表示全部線程都執行完畢,而後在閉鎖上等待的線程就能夠恢復工做了。
相關文章
相關標籤/搜索