簡述線程,程序、進程的基本概念。以及他們之間關系是什么?


1. 簡述線程,程序、進程的基本概念。以及他們之間關系是什么?

    線程與進程相似,但線程是一個比進程更小的執行單位。一個進程在其執行的過程中可以產生多個線程。與進程不同的是同類的多個線程共享同一塊內存空間和一組系統資源,所以系統在產生一個線程,或是在各個線程之間作切換工作時,負擔要比進程小得多,也正因為如此,線程也被稱為輕量級進程。

    程序是含有指令和數據的文件,被存儲在磁盤或其他的數據存儲設備中,也就是說程序是靜態的代碼。

    進程是程序的一次執行過程,是系統運行程序的基本單位,因此進程是動態的。系統運行一個程序即是一個進程從創建,運行到消亡的過程。簡單來說,一個進程就是一個執行中的程序,它在計算機中一個指令接着一個指令地執行着,同時,每個進程還占有某些系統資源如CPU時間,內存空間,文件,文件,輸入輸出設備的使用權等等。換句話說,當程序在執行時,將會被操作系統載入內存中。

    線程 是 進程 划分成的更小的運行單位。線程和進程最大的不同在於基本上各進程是獨立的,而各線程則不一定,因為同一進程中的線程極有可能會相互影響。從另一角度來說,進程屬於操作系統的范疇,主要是同一段時間內,可以同時執行一個以上的程序,而線程則是在同一程序內幾乎同時執行一個以上的程序段。

線程上下文的切換比進程上下文切換要快很多

  • 進程切換時,涉及到當前進程的CPU環境的保存和新被調度運行進程的CPU環境的設置。
  • 線程切換僅需要保存和設置少量的寄存器內容,不涉及存儲管理方面的操作。

 

2. 線程有哪些基本狀態?這些狀態是如何定義的?

 

  1. 新建(new):新創建了一個線程對象。
  2. 可運行(runnable):線程對象創建后,其他線程(比如main線程)調用了該對象的start()方法。該狀態的線程位於可運行線程池中,等待被線程調度選中,獲 取cpu的使用權。
  3. 運行(running):可運行狀態(runnable)的線程獲得了cpu時間片(timeslice),執行程序代碼。
  4. 阻塞(block):阻塞狀態是指線程因為某種原因放棄了cpu使用權,也即讓出了cpu timeslice,暫時停止運行。直到線程進入可運行(runnable)狀態,才有 機會再次獲得cpu timeslice轉到運行(running)狀態。阻塞的情況分三種:
    • (一). 等待阻塞:運行(running)的線程執行o.wait()方法,JVM會把該線程放 入等待隊列(waiting queue)中。
    • (二). 同步阻塞:運行(running)的線程在獲取對象的同步鎖時,若該同步 鎖 被別的線程占用,則JVM會把該線程放入鎖池(lock pool)中。
    • (三). 其他阻塞: 運行(running)的線程執行Thread.sleep(long ms)或t.join()方法,或者發出了I/O請求時,JVM會把該線程置為阻塞狀態。當sleep()狀態超時join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入可運行(runnable)狀態。

 

               5.死亡(dead):線程run()、main()方法執行結束,或者因異常退出了run()                      方法,則該線程結束生命周期。死亡的線程不可再次復生。

    

備注: 可以用早起坐地鐵來比喻這個過程(下面參考自牛客網某位同學的回答):
  1. 還沒起床:sleeping
  2. 起床收拾好了,隨時可以坐地鐵出發:Runnable
  3. 等地鐵來:Waiting
  4. 地鐵來了,但要排隊上地鐵:I/O阻塞
  5. 上了地鐵,發現暫時沒座位:synchronized阻塞
  6. 地鐵上找到座位:Running
  7. 到達目的地:Dead      
3. 何為多線程?
     多線程就是多個線程同時運行或交替運行。單核CPU的話是順序執行,也就是交替運行。多 核CPU的話,因為每個CPU有自己的運算器,所以在多個CPU中可以同時運行。
4. 為什么多線程是必要的?
  1. 使用線程可以把占據長時間的程序中的任務放到后台去處理。
  2. 用戶界面可以更加吸引人,這樣比如用戶點擊了一個按鈕去觸發某些事件的處理,可以彈出一個進度條來顯示處理的進度。
  3. 程序的運行速度可能加快。
5 使用多線程常見的三種方式
①繼承Thread類
   MyThread.java
  
public class MyThread extends Thread {
         @Override
         public void run() {
                super.run();
                System.out.println("MyThread");
         }
}

  Run.java

public class Run {

          public static void main(String[] args) {
                   MyThread mythread = new MyThread();
                   mythread.start();
                   System.out.println("運行結束");
          }
}

運行結果:  從上面的運行結果可以看出:線程是一個子任務,CPU以不確定的方式,或者說是以隨機的時間來調用線程中的run方法。

③使用線程池

在《阿里巴巴Java開發手冊》“並發處理”這一章節,明確指出線程資源必須通過線程池提供,不允許在應用中自行顯示創建線程。

為什么呢?

       使用線程池的好處是減少在創建和銷毀線程上所消耗的時間以及系統資源開銷,解決資源不足的問題。如果不使用線程池,有可能會造成系統創建大量同類線程而導致消耗完內存或者“過度切換”的問題。

另外《阿里巴巴Java開發手冊》中強制線程池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險

 

Executors 返回線程池對象的弊端如下:

 

 

 

  • FixedThreadPool 和 SingleThreadExecutor : 允許請求的隊列長度為 Integer.MAX_VALUE,可能堆積大量的請求,從而導致OOM。
  • CachedThreadPool 和 ScheduledThreadPool : 允許創建的線程數量為 Integer.MAX_VALUE ,可能會創建大量線程,從而導致OOM。

 

6 線程的優先級

每個線程都具有各自的優先級, 線程的優先級可以在程序中表明該線程的重要性,如果有很多線程處於就緒狀態,系統會根據優先級來決定首先使哪個線程進入運行狀態。但這個並不意味着低 優先級的線程得不到運行,而只是它運行的幾率比較小,如垃圾回收機制線程的優先級就比較低。所以很多垃圾得不到及時的回收處理。
線程優先級具有繼承特性。 比如A線程啟動B線程,則B線程的優先級和A是一樣的。
線程優先級具有隨機性。 也就是說線程優先級高的不一定每一次都先執行完。
Thread類中包含的成員變量代表了線程的某些優先級。如 Thread.MIN_PRIORITY(常數1)Thread.NORM_PRIORITY(常數5), Thread.MAX_PRIORITY(常數10)。其中每個線程的優先級都在 Thread.MIN_PRIORITY(常數1) Thread.MAX_PRIORITY(常數10) 之間,在默認情況下優先級都是 Thread.NORM_PRIORITY(常數5)。
學過操作系統這門課程的話,我們可以發現多線程優先級或多或少借鑒了操作系統對進程的管理。
 
7 Java多線程分類

用戶線程

運行在前台,執行具體的任務,如程序的主線程、連接網絡的子線程等都是用戶線程

守護線程

運行在后台,為其他前台線程服務.也可以說守護線程是JVM中非守護線程的 “佣人”。
  • 特點: 一旦所有用戶線程都結束運行,守護線程會隨JVM一起結束工作
  • 應用: 數據庫連接池中的檢測線程,JVM虛擬機啟動后的檢測線程
  • 最常見的守護線程: 垃圾回收線程
如何設置守護線程?
可以通過調用 Thead 類的 setDaemon(true) 方法設置當前的線程為守護線程。
 
注意事項:
 
1. setDaemon(true)必須在start()方法前執行,否則會拋出IllegalThreadStateException異常
2. 在守護線程中產生的新線程也是守護線程
3. 不是所有的任務都可以分配給守護線程來執行,比如讀寫操作或者計算邏輯
8 sleep()方法和wait()方法簡單對比
  • 兩者最主要的區別在於:sleep方法沒有釋放鎖,而wait方法釋放了鎖 。
  • 兩者都可以暫停線程的執行。
  • Wait通常被用於線程間交互/通信,sleep通常被用於暫停執行。
  • wait()方法被調用后,線程不會自動蘇醒,需要別的線程調用同一個對象上的notify()或者notifyAll()方法。sleep()方法執行完成后,線程會自動蘇醒。
9 為什么我們調用start()方法時會執行run()方法,為什么我們不能直接調用run()方法?
這是另一個非常經典的java多線程面試問題,而且在面試中會經常被問到。很簡單,但是很多人都會答不上來!
new一個Thread,線程進入了新建狀態;調用start()方法,會啟動一個線程並使線程進入了就緒狀態,當分配到時間片后就可以開始運行了。 start()會執行線程的相應准備工作,然后自動執行run()方法的內容,這是真正的多線程工作。 而直接執行run()方法,會把run方法當成一個mian線程下的普通方法去執行,並不會在某個線程中執行它,所以這並不是多線程工作。
 
總結: 調用start方法方可啟動線程並使線程進入就緒狀態,而run方法只是thread的一個普通方法調用,還是在主線程里執行。
 
 
 
                                       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM