Java多線程(一)——多線程基本概念、線程創建方式

Java多線程(一)

一、程序、進程、線程基本概念

程序:是爲完成特定任務,用某種語言編寫的一組指令的集合,即指一段靜態的代碼,靜態對象。
進程:是程序的一次執行過程,或是正在運行的一個程序,是一個動態的過程,有它自身的產生,存在和消亡的過程。
線程:線程是操作系統能夠進⾏運算調度的最⼩單位,它被包含在進程之中,是進程中的實際運作單位,可以使⽤多線程對
進⾏運算提速。

二、併發、並行基本概念

並行:多個CPU同時執行多個任務。多個cpu實例或者多臺機器同時執行一段處理邏輯。
併發:一個CPU(採用時間片)同時執行多個任務。通過cpu調度算法,讓用戶看上去同時執行,實際上從cpu操作層面不是真正的同時。

三、Java多線程基本狀態及生命週期

多線程有五種基本狀態,分別是新建狀態、就緒狀態,運行狀態,死亡狀態,阻塞狀態。

線程的基本狀態:

  • 新建 :從新建一個線程對象到程序start() 這個線程之間的狀態,都是新建狀態;
  • 就緒 :線程對象調用start()方法後,就處於就緒狀態,等到JVM裏的線程調度器的調度;
  • 運行 :就緒狀態下的線程在獲取CPU資源後就可以執行run(),此時的線程便處於運行狀態,運行狀態的線程可變爲就緒、阻塞及死亡三種狀態。
  • 等待/阻塞/睡眠 :在一個線程執行了sleep(睡眠)、suspend(掛起)等方法後會失去所佔有的資源,從而進入阻塞狀態,在睡眠結束後可重新進入就緒狀態。
  • 終止 :run()方法完成後或發生其他終止條件時就會切換到終止狀態。

在這裏插入圖片描述

四、Java多線程常用方法

  1. start():1.啓動當前線程2.調用線程中的run方法
  2. run():通常需要重寫Thread類中的此方法,將創建的線程要執行的操作聲明在此方法中
  3. currentThread():靜態方法,返回執行當前代碼的線程
  4. getName():獲取當前線程的名字
  5. setName():設置當前線程的名字
  6. yield():主動釋放當前線程的執行權
  7. join():在線程中插入執行另一個線程,該線程被阻塞,直到插入執行的線程完全執行完畢以後,該線程才繼續執行下去
  8. stop():過時方法。當執行此方法時,強制結束當前線程。
  9. sleep(long millitime):線程休眠一段時間
  10. isAlive():判斷當前線程是否存活

五、多線程創建方式

(一)繼承Thread類:

  1. 創建一個集成於Thread類的子類
  2. 重寫Thread類的run( )方法
  3. 創建Thread子類的對象
  4. 通過此對象調用start( )方法

start( )方法與run( )方法的區別:

start( )方法:

​ 用start方法來啓動線程,真正實現了多線程運行,這時無需等待run方法體代碼執行完畢而直接繼續執行下面的代碼。通過調用Thread類的start()方法來啓動一個線程,這時此線程處於就緒(可運行)狀態,並沒有運行,一旦得到cpu時間片,就開始執行run()方法,這裏方法run()稱爲線程體,它包含了要執行的這個線程的內容,run方法運行結束,此線程隨即終止。

Java源碼start( )方法:
		public synchronized void start() {
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
  
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

    private native void start0();

run( )方法:

run()方法只是類的一個普通方法而已,如果直接調用Run方法,程序中依然只有主線程這一個線程,其程序執行路徑還是隻有一條,還是要順序執行,還是要等待run方法體執行完畢後纔可繼續執行下面的代碼,這樣就沒有達到寫線程的目的。

Java源碼run( )方法:
		@Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

Thread創建線程實例:

public class Main {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();

        t1.setName("t1");
        t2.setName("t2");

        t1.start();
        t2.start();

        //使用lambda表達式簡化
        new Thread(()->{
            int i = 0;
            while(true){
                System.out.println(Thread.currentThread().getName()+"線程"+i++);
            }
        }).start();
    }
}

class MyThread extends Thread{
    int i = 0;

    @Override
    public void run() {
        while(true){
            System.out.println(Thread.currentThread().getName()+"線程"+i++);
        }
    }
}

(二)實現Runable接口:

  1. 創建一個實現了Runable接口的類
  2. 實現類去實現Runnable中的抽象方法:run()
  3. 創建實現類的對象
  4. 將此對象作爲參數傳遞到Thread類中的構造器中,創建Thread類的對象
  5. 通過Thread類的對象調用start()

實現Runable接口實例:

public class RunnableTest {
    public static void main(String[] args) {
        MyRunnable m1 = new MyRunnable();
        Thread t1 = new Thread(m1);
        Thread t2 = new Thread(m1);

        t1.start();
        t2.start();

        //局部內部類
        class Test2 implements Runnable{
            @Override
            public void run() {
                for(int i=0;i<5;i++) {
                    System.out.println("局部內部類");
                }
                System.out.println("-------------");
            }
        }
        new Thread(new Test2()).start();

        //匿名內部類
        new Thread(new Runnable(){
            @Override
            public void run() {
                for(int i=0;i<5;i++) {
                    System.out.println("匿名內部類");
                }
                System.out.println("-------------");
            }
        }).start();
    }
}

class MyRunnable implements Runnable{
    @Override
    public void run() {
            for(int i=0;i<5;i++){
                System.out.println(Thread.currentThread().getName()+"線程"+i++);
            }
        }
    }

(三)實現callable接口:

與實現Runable接口相比:
  1. runnable重寫的run方法不如callaalbe的call方法強大,call方法可以有返回值
  2. 方法可以拋出異常
  3. 支持泛型的返回值
  4. 需要藉助FutureTask類,比如獲取返回結果
使用Callable創建線程:
  1. 創建一個實現callable的實現類
  2. 實現call方法,將此線程需要執行的操作聲明在call()中
  3. 創建callable實現類的對象
  4. 將callable接口實現類的對象作爲傳遞到FutureTask的構造器中,創建FutureTask的對象
  5. 將FutureTask的對象作爲參數傳遞到Thread類的構造器中,創建Thread對象,並調用start方法啓動(通過FutureTask的對象調用方法get獲取線程中的call的返回值)

實現callable接口實例:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableTest {
    public static void main(String[] args){
        //new一個實現callable接口的對象
        NumThread numThread = new NumThread();

        //通過futureTask對象的get方法來接收futureTask的值
        FutureTask futureTask = new FutureTask(numThread);

        Thread t1 = new Thread(futureTask);
        t1.setName("線程1");
        t1.start();

        try {
            //get返回值即爲FutureTask構造器參數callable實現類重寫的call的返回值
            Object sum = futureTask.get();
            System.out.println(Thread.currentThread().getName()+":"+sum);
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class NumThread implements Callable {

    private int sum=0;//

    @Override
    public Object call() throws Exception {
        for(int i=0;i<5;i++){
            sum += i;
            System.out.println(Thread.currentThread().getName()+"線程"+i++);
        }
        return sum;
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章