太牛了!第一次看到有大佬把Java線程與進程的這麼詳細的,我先收藏了!

線程與進程

概念

進程:進程指的是一段正在運行的程序
線程:一個程序運行中能夠執行多個任務,任務就稱之爲線程。
進程能夠有多個線程,各個線程之間共享程序的內存空間面試

爲何有了進程,還會出現線程?

每一個進程有本身獨立的地址空間,多併發請求,爲每個請求建立一個進程,致使系統開銷、用戶請求效率低算法

區別

每一個進程有用本身獨有的變量,線程共享數據,線程之間的通訊相比於進程之間的通訊更加有效,更加容易
線程相比於進程建立/銷燬開銷 更小
進程是資源分配的最小單位,線程是CPU調度的最小單位
多進程程序更加健壯,多線程程序只要有一個線程掛掉,對其共享資源的其餘線程也回產生影響
若是追求速度,追求系統穩定選擇線程;若是頻繁的建立和銷燬,選擇線程;spring

線程的建立

說完線程的基本知識,那麼久要談談在應用中,咱們應該如何建立線程,實際應用中建立線程主要有如下四種方法,以下:數據庫

方法一:繼承Thread類,重寫run()方法

class  MyThread extends  Thread{
    @Override
    public void run() {
        while(true){
            System.out.println("eat food");
        }
    }
}
public class TestDemo1 {
    public static void main(String[] args) {
        //邊吃飯邊看電視
        //建立子線程對象
        Thread thread = new MyThread();
        //啓動吃飯的thread
         thread.run();
         //main線程
        while(true){
            System.out.println("watch Tv");
        }
    }
  }

方法二:實現Runable接口

class  MyRunnable implements  Runnable{
    @Override
    public void run() {
        while(true){
            System.out.println("eat food");
        }
    }
}
public class TestDemo1 {
    public static void main(String[] args) {
    //實現runnable接口 建立子線程  建立子線程對象        Thread thread = new Thread(new MyRunnable());    
    thread.start();
    }
  }

方法三:匿名線程 使用匿名內部類

//匿名內部類   建立線程
            new Thread(){
                @Override
                public void run() {
                 System.out.println("thread 0");
                }
            }.start();

方法四:實現Callable接口,重寫call()方法

利用Callable建立時,步驟較爲複雜,具體以下:
a.建立Callable接口的實現類,重寫call方法
b.建立Callable實現類的實例,使用FutureTask包裝該實例
c.將FutureTask實例做爲參數建立線程對象
d.啓動該線程
e.調用FutureTask的get方法獲取子線程的執行結果設計模式

class MyCallable implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for(int i=0; i<10000; i++){
            sum += i;
        }
        return sum;
    }
}
public class TestDemo1 {
    public static void main(String[] args) {
     //建立Callable接口的實現類    重寫call 方法
        //建立Callable實現類的實例
        Callable<Integer> callableTask = new MyCallable();
        //使用FutureTask包裝實例
        FutureTask<Integer> task = new FutureTask<>(callableTask);
        //Future實例做爲參數建立線程對象
        Thread thread = new Thread(task);
        //啓動該線程
        thread.start();
        //調用FutureTask的get 方法獲取子線程的執行結果
        try {
            Integer integer = task.get();
            System.out.println("result"+integer);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

可能有人會思考,這裏一樣使用Runable和Callable接口來建立線程,那麼這二者各有什麼不一樣呢?能想到這,可謂是很用心啦,這也是這裏的一個面試考點吶!!!
Runable 接口和 Callable 接口的區別?
緩存

這裏就簡單說一下二者的區別,可是僅僅單獨比較兩個接口的區別實際意義並不大,而且也不是很全面,以後會
詳細的分析兩個接口的區別,在更深層次上去理解兩個接口。你們繼續關注吖!數據結構

線程的生命週期及經常使用方法解析

生命週期

線程的生命週期主要包括如下六種狀態:新建狀態、就緒狀態、阻塞狀態、等待狀態、睡眠狀態、終止狀態。多線程

一、new() :新建狀態  new關鍵字建立一個線程對象,它並非出於執行狀態 並未執行START方法啓動線程
  二、runnable() :就緒狀態   等待被執行  線程對象調用START方法,纔是JVM中真正建立了一個線程,
  建立好的線程並非一經啓動,就會當即執行。該狀態的全部線程會位於就緒線程池中,等待當前操做系統的資源,例如CPU,
  得到CPU的使用權。
  三、blocked(): 阻塞狀態 等待一個監視器鎖進入同步代碼塊或者同步方法,代碼塊和方法指某一時刻
  只可以有一個線程去執行,其餘線程只能等待。
  四、Waiting(): 等待狀態  Object.wait()/Thread.join()/LockSupport.park() 都會使得當前從Runnable
  轉換爲Waiting 狀態    調用waiting()  會釋放monitor Lock
  五、TimeWaiting():睡眠狀態    
   調用Object.wait(long miles)/Thread.sleep(long miles)/LockSupport.parkNano()/LockSupport.parkUntile()
  六、Terminated():終止狀態  是一個線程的最終狀態  線程若是進入此狀態,意味着該線程結束。

簡單瞭解線程的生命週期以後,那咱們繼續學習線程六狀態之間的轉換:併發

經常使用方法解析分佈式

1) start()

啓動一個線程,將線程添加到線程組中,線程狀態會從New 狀態轉換到Runnable 狀態,而後獲取CPU以後進入Running狀態執行run();

2)sleep()

靜態方法 ,存在兩個重載函數

public static native void sleep(long millis)
 public static void sleep(long millis, int nanos)

做用:sleep()方法使得當前線程進入睡眠狀態並指定休眠時間,暫停執行;
sleep()方法不會釋放Monitor Lock 使用權;
JDK1.5以後,引入枚舉類TimeUnit ,其對sleep進行封裝 直接使用從而省去時間換算的步驟,不用將時間轉爲ms,更加的方便使用。

3)yield()

public static native void yield();

做用: 提醒CPU調度器 ,我當前的線程願意放棄當前的CPU資源(屬於啓發式方法),若是當前CPU資源不緊張,會忽略這種提醒。
yield()方法不會釋放Monitor Lock 使用權;

4)join()

join() 一直等待
join(long millis) 等待指定毫秒數
join(long millis, int nanos) 等待指定毫秒數

含義:在線程B中join某個線程A,會使得B線程進入等待,直到線程A結束生命週期,或者達到給定的時間,在這給定時間期間線程B會處於等待狀態.

5)wait()

調用某個對象的wait()方法可讓當前線程阻塞

6)notify()

調用當前對象notify/notifyAll纔可以喚醒這個對象所在的線程。

7)notifyAll()

將鎖對象等待池中全部線程移動到鎖標誌等待池中。
注意:使用這三個方法須要讓當前線程擁有當前對象的monitor lock

8)線程中斷方法

每一個Java線程都會有一箇中斷狀態位,程序能夠檢測這個中斷狀態位判讀線程是否執行結束。有如下三種方法:
interrupt()
public void interrupt() 由線程對象調用,將中斷位置置爲true

public void interrupt();

以下方法可以使得當前線程進入阻塞狀態,調用interrupt方法能夠打斷阻塞,所以這種方法被稱之爲可中斷方法

Object.wait()/wait(long)
Thread.sleep(long)/TimUnit.XXX.sleep(long)
Thread.join()/Thread.join(long)

若是一個線程被interrupt,設置interrupt flag;若是當前線程正在執行可中斷方法,調用interrupt方法,反而致使interrupt flag被清除.

isInterrupted

public boolean isInterrupted();

實例方法,判斷當前線程的中斷狀態位是否爲true,判斷進程是否被中斷。

interrupted()

public static boolean interrupted();

靜態方法,調用interrupted會擦除中斷狀態位的標識,判斷進程是否被中斷

interrupted和isInterrupted方法的區別:

1)interrupted()是靜態方法,isInterrupted()是實例方法;
2)interrupted()會清空線程中斷狀態,isInterrupted()不會清空線程中斷狀態。

最後

歡迎關注公衆號:前程有光,領取一線大廠Java面試題總結+各知識點學習思惟導+一份300頁pdf文檔的Java核心知識點總結! 這些資料的內容都是面試時面試官必問的知識點,篇章包括了不少知識點,其中包括了有基礎知識、Java集合、JVM、多線程併發、spring原理、微服務、Netty 與RPC 、Kafka、日記、設計模式、Java算法、數據庫、Zookeeper、分佈式緩存、數據結構等等。

相關文章
相關標籤/搜索