7.併發編程

 1.線程和進程的關係?

一個進程可以有多個線程,但必須有一個線程,一個線程只能在一個進程的範圍內活動。

 

2.併發和並行的區別?

併發是指有公共資源的爭奪,並行是沒有公共資源的爭奪。

 

3.實現多線程的方式?

    1>.繼承Thred類,重寫run接口。

    2>.實現Runnable接口,實現run方法。

    3>.通過Callable接口,實現call方法。

 

4.什麼是守護線程?

專門用於服務其他的線程,如果其他的線程(即用戶自定義線程)都執行完畢,連main線程也執行完畢,那麼jvm就會退出(即停止運行)——此時,連jvm都停止運行了,守護線程當然也就停止執行了

 

5. synchoried原理?

這個原本是個重量級鎖,但是在後來更新中進行了優化,引入了偏向鎖和輕量級鎖。通過鎖膨脹一步步升級爲重量級鎖。

以前的文章:https://blog.csdn.net/huaixiaohai_1/article/details/92607179

還是說原理吧:JVM基於進入和退出Monitor對象來實現同步和代碼塊同步,同步代碼塊是通過monitorenter和monitorexit實現的。同步方法是通過另一種方式實現的(JVM規範中並沒有說明)。但是還是可以使用這種方法,monitorenter指令是在編譯後插入到同步代碼塊的開始位置,而monitorexit插入在結束或者異常的位置。必須對應。

 

6.volicate原理?

volicate主要是通過Lock前綴指令來實現的。

lock前綴指令會引起處理器緩存寫回到內存中。當一個處理器的緩存寫回到內存中會導致其他處理器的緩存無效。

還有一個作用是他會禁止指令重排序。

 

7.禁止指令重排序是什麼原理?

通過內存屏障

 

8.原子類實現原理?

以AutomicInteger爲例進行源碼分析:

 // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;

上面這段代碼的意思是:JVM可以實現Java對象的佈局,也就是在內存裏Java對象的各個部分放在哪裏,包括對象的實例字段和一些元數據之類。jdk內部使用的工具類sun.misc.Unsafe提供的方法objectFieldOffset()用於獲取某個字段相對Java對象的“起始地址”的偏移量,可以使用這個偏移量調用getInt、getLong、getObject等方法來獲取某個Java對象的某個字段。
還可以看到value通過volatile保證可見性:

//兩個構造方法 
public AtomicInteger(int initialValue) {
        value = initialValue;
    }
 public AtomicInteger() {
    }
//get和set方法
public final int get() {
        return value;
    }

public final void set(int newValue) {
        value = newValue;
    }

看一下關鍵方法:更新操作

public final int getAndUpdate(IntUnaryOperator updateFunction) {
        int prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsInt(prev);
        } while (!compareAndSet(prev, next));
        return prev;
    }
 public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

他首先備份了一份原來的值,然後通過cas的方式一直while直到成功。

 

9. 信號量機制?

信號量機制是爲了保護共享變量,使得一個線程只有一個時刻只有一個進程訪問資源。

主要通過P(申請資源)V(釋放資源)操作來實現信號量,對信號量的操作都是原子性的。

信號量S:用來記錄資源數量,看是否能滿足申請需要。

P(S) 若 S > 0:則S-1;線程繼續執行,若S<0則掛起線程。

V(S) 若S>0;則S+1;若S<0,則喚醒一個線程。

 

10.AQS是什麼,講講?

抽象的隊列同步器,是除了java自帶的synchronized關鍵字之外的鎖機制。

如果請求的資源空閒,那麼將當前請求資源設置爲有效的工作線程,並將共享資源設置爲鎖定狀態。如果請求的資源鎖定,那麼就進入CLH阻塞隊列。

CLH阻塞隊列:是一個虛擬雙向隊列,不存在實際的隊列,僅記錄節點之間的關聯關係。

實際上AQS就是基於CLH隊列,用volicate修飾共享變量state,線符程通過CAS去修改狀態符。成功則獲取鎖成功,失敗則進入等待隊列。在這裏插入圖片描述

看這個圖就清除多了。

 

11.爲什麼要用線程池?

當併發數量多的時候,頻繁的創建和銷燬線程費時間,所以採用線程池,讓每個線程可以多次使用,減少消耗。

 

12.線程池的種類以及自定義線程池?

java提供了構建線程池,使線程池使用變得簡單起來了。

 public ThreadPoolExecutor(
                              //初始線程數
                              int corePoolSize,
                              //最大線程個數
                              int maximumPoolSize,
                              //生存時間,超過初始線程個數以後的線程池使用後的銷燬時間
                              long keepAliveTime,
                              //時間單位
                              TimeUnit unit,
                              //隊列內部的使用數據結構
                              BlockingQueue<Runnable> workQueue)

    1>.單線程池(newSingleThreadExecutor)

 public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

這個線程池可以清楚的看到初始線和最大線程都只有1個線程。

  2>.定長線程池(newFixedThreadPool)

 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

可以看到最大和初始化的線程數一樣。適合任務量比較固定且時間消耗比較長的

 3>.可緩存線程池(newCachedThreadPool)

 public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

這個線程數量基本可以無限加,但是沒用後60s之後就消失了。比較以用於任務量大但是消耗時間比較小的。

 4>.大小無限制的線程池(newScheduledThreadPool)

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

這個線程池也時固定大小的,構造方法裏邊指定,最大線程數也是無限加,非核心線程創建完以後就銷燬。但是這個線程池使用的DelayedWorkQueue,意味着他會把任務排序,執行完以後循環。適用於定時任務和固定週期的任務。

自定義線程池根據業務需求使用ThreadPoolExecutor去創建,

這些可能也不全,而且理解也不夠透徹,併發編程的水太深了。

發佈了111 篇原創文章 · 獲贊 70 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章