java多線程

一、多線程的意義 

  操做系統能夠多任務執行,每一個任務就是就是一個進程。
  每一個任務(進程)能夠分多工做流分別執行。html

  比較:進程:有獨立的代碼和數據空間(進程上下文),進程切換開銷大,進程是資源分配的最小單位。java

     線程:每一個線程有獨立的運行棧和程序計數器(PC),線程切換開銷小。線程是cpu調度的最小單位。算法

   


 

  並行與併發:緩存

  • 並行:多個cpu實例或者多臺機器同時執行一段處理邏輯,是真正的同時。
  • 併發:經過cpu調度算法,讓用戶看上去同時執行,實際上從cpu操做層面不是真正的同時。

 


 

  進程和線程的生命週期:建立、就緒、運行、阻塞、終止安全

  

    1)wait,notify和notifyAll只能在同步控制方法或者同步控制塊裏面使用,而sleep能夠在任何地方使用
    2) sleep必須捕獲異常,而wait,notify和notifyAll不須要捕獲異常。sleep()睡眠時,保持對象鎖,仍然佔有該鎖;而wait()睡眠時,釋放對象鎖。
    3)wait、sleep和join均可以用interrupt()打斷,若是此刻線程B正在wait/sleep /join,則線程B會馬上拋出InterruptedException,在catch() {} 中直接return便可安全地結束線程。多線程

 

二、java多線程的實現

  Java線程的實現是基於一對一的線程模型,所謂的一對一模型,實際上就是經過語言級別層面程序去間接調用系統內核的線程模型,即咱們在使用Java線程時,Java虛擬機內部是轉而調用當前操做系統的內核線程來完成當前任務。併發

  因爲咱們編寫的多線程程序屬於語言層面的,程序通常不會直接去調用內核線程,取而代之的是一種輕量級的進程(Light Weight Process),也是一般意義上的線程,因爲每一個輕量級進程都會映射到一個內核線程,所以咱們能夠經過輕量級進程調用內核線程,進而由操做系統內核將任務映射到各個處理器,這種輕量級進程與內核線程間1對1的關係就稱爲一對一的線程模型。ide

  每一個線程最終都會映射到CPU中進行處理,若是CPU存在多核,那麼一個CPU將能夠並行執行多個線程任務。性能

  

  建立多線程的方式:ui

    1)繼承Thread類:new myThread(「A線程」).start();

    2)實現Runnable接口:new Thread(new myThread("A線程")).start();

    3)實現Callable接口

 

 

三、java多線程的內存模型

 

     前面說了,線程是cpu的最新調度單位,即cup在執行一個任務(進程)時,是執行的進程中的更小粒度線程。

  執行方式:1)(建立)線程對象。即爲該線程分配內存並初始化,其中一個工做就是拷貝主存中的共享變量到線程工做內存(拷貝時:對於基本類型,直接拷貝值;對於引用類型,拷貝引用變量,其值仍保存堆中);

       2)線程調用start()方法(就緒)。

       3)線程獲取到cpu時間片後,恢復cpu(運行)現場。即將線程的相關數據寫入cpu緩存,再到cpu寄存器等進行執行。

       4)(結束)將在cpu中處理後的數據刷新到主存。而後空出的cpu繼續加載其餘線程的數據進行執行。

public class MyThread extends Thread{
    
     int a=1;//多線程中的共享數據(基本數據類型),運行時直接拷貝值到線程工做內存
     String str;//多線程中的共享數據(引用類型),運行時拷貝引用變量到線程工做內存
      MyThrad(String str){this.str = str;}

      @Override
       public void run(){
            str=str+a;
      }

      public static void main(String[] args){
          String str=new String("hgp");
           //啓動線程後會拷貝共享變量到各自線程的工做內存,處理完了再分別刷新到主存(就是上面堆內存中的str對象中)  
           new Mythread(str).start();
           new Mythread(str).start();  
    }  
  
}
View Code

 

  

 

四、線程同步

  不論是什麼內存模型,最終仍是運行在計算機硬件上的。當一個CPU須要訪問主存時,會先讀取一部分主存數據到CPU緩存,進而在讀取CPU緩存到寄存器。當CPU須要寫數據到主存時,一樣會先flush寄存器到CPU緩存,而後再在某些節點把緩存數據flush到主存。

      

 

  前提:上面說了對於線程的執行,並非直接進行的,主存共享變量的操做要通過讀入線程內存、cpu緩存等,待數據在cpu中執行完了再通過緩存、線程內存寫入主存。也就是說線程的執行有一個時間過程,在這個過程當中多線程對同一代碼塊的執行是彼此獨立的,由於cpu操做的操做數已是主存數據的拷貝了。

  1)這樣的話如果非原子性操做就易出現錯誤,解決方法——線程同步,經過對象鎖機制實現,用關鍵字synchronized聲明。

    synchronized關鍵字的鎖機制,鎖的是對象:

        當synchronized使用在方法上或成員變量上,鎖的是該實例對象;

        當synchronized使用在static靜態成員上,鎖的是該類型的Class對象。

        當synchronized使用在自定義代碼塊上,能夠自定義執行該代碼塊須要的鎖對象。

    注:synchronized鎖的是對象,不一樣對象鎖不一樣,如當訪問一個同步方法時,該對象的其餘同步方法也被鎖;普通同步方法與靜態同步方法互不干擾。還有synchronized關鍵字不能被繼承。

  2)在線程把操做的共享數據刷新到主存以前,對共享數據的更改對其餘線程來講是不可見的,解決方法——volatile 關鍵字,保證變量會直接從主存讀取,而對變量的更新也會直接寫到主存。

        voilatile只是解決可見性,對於非原子操做並不保證線程安全。

 

  總結:原子性——原子性指的是一個操做是不可中斷的,即便是在多線程環境下,一個操做一旦開始就不會被其餘線程影響。

     可見性——指的是當一個線程修改了某個共享變量的值,其餘線程是否可以立刻得知這個修改的值。

     重排序——計算機在執行程序時,爲了提升性能,編譯器和處理器的經常會對指令作重排。volatile會禁用重排序。

     有序性——指的是單線程內保證串行語義執行的一致性,後半句則指指令重排現象和工做內存與主內存同步延遲現象。

 

參考:http://blog.csdn.net/suifeng3051/article/details/52611310

   http://blog.csdn.net/javazejian/article/details/72772461

   https://www.cnblogs.com/wxd0108/p/5479442.html

   https://www.cnblogs.com/GarfieldEr007/p/5746362.html

相關文章
相關標籤/搜索