Java併發編程相關知識整理

一、什麼是進程、線程、多線程?
     進程當一個程序開始運行時,它就是一個進程,進程包括運行中的程序和程序所使用到的內存和系統資源。進程間通信依靠IPC資源,例如管道、套接字
     線程是程序中的一個執行流,每一個線程都有本身的專有寄存器(棧指針、程序計數器等),但代碼是共享的,即不一樣的線程能夠執行一樣的函數。
     多線程是指程序中包含多個執行流,即在一個程序中能夠同時運行多個不一樣的線程來執行不一樣的任務,也就是說說容許單個程序建立多個並行執行的線程來完成各自的任務。線程間通信依靠JVM提供的API,例如wait()、notify、notifyAll等方法,線程間還能夠經過共享的主內存來進行值的傳遞java

二、多線程的優缺點?
    優勢:能夠提升CPU的利用率。在多線程程序中,一個線程必須等待的時候,CPU能夠運行其餘的線程而不是等待,提升程序相應效率。
    缺點:線程也是程序,全部線程須要佔用內存,線程越多佔用的內存也越多。
             線程須要協調和管理,因此須要CPU時間跟蹤線程
             線程之間對共享資源的訪問會互相影響,必需要解決競用共享資源的問題
             線程太多會致使控制太複雜,最終可能形成不少Bug服務器

三、多線程必定比單線程快嗎?
     不必定,因爲多線程會存在線程上下文切換,會致使程序執行速度變慢,但能夠充分利用CPU,因此對於用戶來講,能夠減小用戶響應時間。
好比,嘗試使用並行和串行分別執行累加的操做觀察是否並行執行必定比串行更快:多線程

package com.test.demo;

public class Tester {

    private static final long count = 1000000000;
    
    public static void bingxing() throws Exception {
        long startTime = System.currentTimeMillis();
        
        //經過匿名內部類來建立線程
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                int a = 0;
                for(long i = 0; i < count; i++) {
                    a += 1;
                }
            }            
        });
        
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                int b = 0;
                for(long k = 0;k < count; k++) {
                    b+=1;
                }
            }            
        });
        
        //啓動線程
        thread1.start();
        thread2.start();
        
        //等待線程結束
        thread1.join();
        thread2.join();
        
        long time = System.currentTimeMillis() - startTime ; 
        System.out.println("並行花費時長:" + time + "ms");
    }
    
    public static void chuanxing() {
        long startTime = System.currentTimeMillis();
        int a = 0;
        for(long i = 0; i < count; i++) {
            a += i;
        }
        int b = 0;
        for(long k = 0;k < count; k++) {
            b+=k;
        }
        long time = System.currentTimeMillis() - startTime ; 
        System.out.println("串行花費時長:" + time + "ms");
    }
    
    public static void main(String[] args) throws Exception {
        bingxing();
        chuanxing();    
    }

}
循環次數 串行執行/ms 並行執行/ms 結果
1千 0ms 2ms
1萬 0ms 2ms
10萬 3ms 4ms
100萬 6ms 4ms
1000萬 13ms 11ms
1億 89ms 78ms

    從測試結果看出當超過100萬次循環後,並行執行的優點越加明顯,不超過100萬次循環時,串行執行的速率要比並行執行的速率高,緣由就是多線程有上下文切換的開銷。ide

四、阻塞與非阻塞
     阻塞和非阻塞一般用來形容多線程之間的相互影響
     阻塞是指一個線程佔用了臨界區資源,那麼其餘全部須要這個資源的線程就不洗在這個臨界區中進行等待,等待會致使線程掛起,這種狀況就是阻塞
     非阻塞強調沒有一個線程能夠妨礙其餘線程執行,全部線程都會嘗試不斷向前執行函數

五、臨界區
     臨界區用來表示一種公共資源或者共享資源能夠被多個線程使用,可是每一次只能有一個線程使用它,一旦臨界區資源被佔用,其餘線程想要使用這個資源,就必須等待測試

六、死鎖Deadlock、飢餓starvation、活鎖Livelock
     死鎖表示兩個或兩個以上的進程在執行過程當中,因爲競爭資源或者因爲彼此通訊而形成的一種阻塞的現象,若無外力做用,它們將沒法推動下去,此時稱系統處於死鎖狀態,這些永遠在互相等待的進程成爲死鎖進程
     飢餓表示一個或者多個線程應爲種種緣由沒法得到所須要的資源,致使一直沒法執行。致使飢餓的緣由多是該線程優先級過低,而高優先級的線程不但搶佔它所須要的資源,致使其沒法向前推動,另一種多是,某線程一直佔着關鍵資源不放,致使其餘須要這個資源的線程沒法正常執行
    活鎖表示兩個或者多個線程主動將資源釋放給其餘線程,致使沒有一個線程能夠同時拿到全部資源而正常執行。spa

七、如何避免死鎖
    指定獲取鎖的順序線程

八、sleep()與wait()區別
     sleep()是Thread類的靜態方法,使當前線程睡眠n毫秒,線程進入阻塞狀態。當睡眠時間到了,解除阻塞,進行可運行狀態,等待CPU的到來。睡眠不釋放鎖。
     wait()是Object的方法,必須和synchronized關鍵字一塊兒使用,線程進入阻塞狀態,當notify或者notifyall被調用後,會解除阻塞。可是,只有從新佔用互斥鎖以後纔會進入可運行狀態。睡眠時,釋放互斥鎖。指針

九、synchronized關鍵字底層實現
     進入時,執行monitorenter,將計數器+1,釋放鎖monitorexit時,計數器-1;當一個線程判斷到計數器爲0時,則當前鎖空閒,能夠佔用,反之,當前線程進入等待狀態。code

十、volatile關鍵字功能
       直接與主內存產生交互,進去讀寫操做,保證可見性;禁止JVM進行指令重排序;能使一個非原子操做變爲原子操做。好比對一個volatile型的long或者douuble變量的讀寫是原子

十一、ThreadLocal關鍵字
       當使用ThreadLocal維護變量時,其爲每個使用該變量的線程提供獨立的變量副本,因此當每個線程均可以獨立的改變本身的副本,而不會影響其餘線程對應的副本

十二、線程池的瞭解
       java.util.concurrent.ThreadPoolExcutor類就是一個線程池。客戶端調用ThreadPoolExecutor.submit(Runable Task)提交任務,線程池內部維護的工做者線程的數量就是該線程池的線程池大小。
       當前線程池大小:表示線程池中實際工做者線程的數量
       最大線程池大小:表示線程池張容許純在的工做者線程的數量上限
       核心線程大小:表示一個不大於最大線程池大小的工做者線程數量上限
      線程池有三種狀態:
      ①、若是運行的線程小於核心線程大小,則Executor始終首選添加新的線程,而不進行排隊
      ②、若是運行的線程等於或者多於核心線程大小,則Executor始終首選將請求加入隊列,而不是添加新線程;
      ③、若是沒法將請求加入隊列,即隊列已經滿了,則建立新的線程,除非建立此線程超過最大線程池大小,在這種狀況下,任務將會被拒絕

1三、線程池的做用
       減小建立和銷燬線程的次數,每一個工做線程均可以被重複利用,可執行多個任務
       能夠根據系統的承受能力,調整線程中工做線程的數目,防止由於消耗過多的內存,而把服務器過載

1四、建立線程的實現方式
       繼承Thread類,實現Runable接口,實現Callable接口

1五、run()方法和start()方法的區別
      start()方法會新建一個線程並讓這個線程執行run()方法;而直接調用run()方法只是做爲一個普通的方法調用而已,它只是會在當前線程中,串行執行run()中的代碼

相關文章
相關標籤/搜索