線程解析1-狀態分析

線程

線程與進程之間的關係及爲何須要多線程放到進程相關文章中進行詳解java

線程的狀態

查詢Thread的源碼能夠看到,內部有一個State的枚舉類,內部有線程的六個狀態web

public enum State {
        //線程新生
        NEW,
        //運行
        RUNNABLE,
        //阻塞
        BLOCKED,
        //等待 死死的等 須要別人喚醒 
        WAITING,
        //超時等待 自動啓動
        TIMED_WAITING,
        //終止
        TERMINATED;
    }
狀態的互相切換

img

建立線程

繼承Thread類
public class Start {
    public static void main(String[] args) {
        MyThread thread1 = new MyThread("thread#1");
        MyThread thread2 = new MyThread("thread#2");
        thread1.start();
        thread2.start();
    }
}

class MyThread extends Thread{
    public MyThread(String name) {
        super(name);
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "print:"+ i);
        }
    }
}
實現Runnable接口
  • 實現Runnable接口的類
public class Start {
    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();
        new Thread(thread1).start();
        new Thread(thread2).start();
    }
}

class MyThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "print:"+ i);
        }
    }
}
  • 匿名內部類
public class Start {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName() + "print:"+ i);
                }
            }
        },"A").start();
        //使用lambda表達式
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + "print:"+ i);
            }
        },"B").start();
    }
}
實現Callable接口

image-20200423185934102.png

咱們從文檔能夠看出,Callable接口與Runnable接口的不一樣:數據庫

  • Callable有返回值
  • Callable能夠拋出異常
  • Runnable是會執行run()方法,而Callable是執行call()方法

那麼Callable接口怎樣才能開啓線程呢?緩存

  • 咱們從Thread的構造方法中能夠發現,只能接收Runnable接口,那咱們從Runnable找起

image-20200423190854562.png

能夠發現Runnable有個FutureTask的實現類,這個類可使Runnable接口返回結果,也能夠執行Callable接口多線程

image-20200423191028916.png

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //new Thread(new Runnable() ).start();
        //    V 就是返回值的類型
        //new Thread(new FutureTask<V>(callable)).start;

        Mythread mythread = new Mythread();
        FutureTask futureTask = new FutureTask(mythread);//適配類

        new Thread(futureTask, "A").start();
        new Thread(futureTask, "B").start();//因爲結果會緩存,提升效率,因此只會打印一個call
        Integer o = (Integer)futureTask.get(); //獲取返回結果,這個get 方法可能會產生阻塞,會等待線程處理結果的方程。
        //或者使用異步通訊 來處理
        System.out.println(o);
    }
}
class Mythread implements Callable<Integer>{
    @Override
    public Integer call() {
        System.out.println("call()");
        return 1024;
    }
}

線程等待(sleep,wait)

兩個均可以讓線程陷入等待,他們的區別以下:異步

  • 來自不一樣的類ide

    • wait來自Object類 sleep來自Thread類
  • 關於鎖的釋放this

    • wait會釋放鎖 sleep不會釋放鎖
  • 使用範圍是不一樣的spa

    • wait必須在同步代碼塊中, sleep能夠在任何地方
  • 是否須要捕獲異常線程

    • wait不須要捕獲異常 sleep須要捕獲異常

線程切換(wait,notify)

利用一個常見的生產者消費者模式來演示wait(),notify()

public class PC {
    public static void main(String[] args) {
        Data data = new Data();

        new Thread(()->{
            for (int i = 0; i < 50; i++) {
                try {
                    data.produce();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 50; i++) {
                try {
                    data.consume();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}
class Data{
    private static int number = 0;
    void produce() throws InterruptedException {
            synchronized (this) {
                if (number != 0) {
                    this.wait();
                }
                number++;
                System.out.println(Thread.currentThread().getName() + "生產" + number);
                this.notify();
            }
    }

    synchronized void consume() throws InterruptedException {
        synchronized (this){
            if (number == 0){
                this.wait();
            }
            number--;
            System.out.println(Thread.currentThread().getName() + "消費" + number);
            this.notify();
        }
    }
}

以上代碼能夠順利讓線程交替執行,可是有一個地方在多線程的時候會出現BUG。

image-20200423162646185.png

在有着更多的線程時,要注意wait條件的一個判斷。

線程禮讓(yield)

一、禮讓線程,讓當前正在執行的線程暫停,但不阻塞

二、將線程從運行狀態轉爲就緒狀態

三、讓CPU從新調度,禮讓不必定成功

public class Yield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();
        new Thread(myYield, "A").start();
        new Thread(myYield, "B").start();
    }
}

class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + ":線程執行");
        Thread.yield();
        System.out.println(Thread.currentThread().getName() + ":線程中止");
    }
}

該代碼會出現禮讓成功與禮讓不成功的狀況,能夠本身嘗試一下。

線程強制插入(join)

等待此線程執行完以後,再執行其餘線程

public class JoinTest implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("插隊開始。。。" + i); 
        }
    }
    public static void main(String[] args) throws InterruptedException {
        JoinTest joinTest = new JoinTest();
        Thread thread = new Thread(joinTest);
        thread.start();

        for (int i = 0; i < 1000; i++) {
            if (i == 200){
                thread.join();
            }
            System.out.println("main..." + i);
        }
    }
}

線程中止

一、使用線程標誌位讓線程中止下來

public class StopTest implements Runnable {
    private boolean flag = false;
    @Override
    public void run() {
        int i = 0;
        while (!flag){
            System.out.println("run..." + i++);
        }
    }
    public static void main(String[] args) throws InterruptedException {
        StopTest stopTest = new StopTest();
        Thread thread = new Thread(stopTest);
        thread.start();

        for (int i = 0; i < 15; i++) {
            System.out.println("main"+i);
            Thread.sleep(10);
            if (i == 8){
                stopTest.stop();
                System.out.println("線程中止");
            }
        }
    }

    public void stop(){
        this.flag = true;
    }
}

二、使用interrupt方法中斷線程

  • 利用後兩種能夠判斷狀態(就是下面圖片中的後兩種方法)

    • 處於中斷狀態時,能夠中止線程的邏輯操做(相似於標誌位)
  • 線程中止-拋異常法

    • 先sleep,後interrupt

      • 若線程處於阻塞狀態,調用interrupt後,會直接拋出異常
    • 先interrupt後sleep

      • 處於中斷狀態後,進入sleep等阻塞狀態時,會拋出異常而且中斷

image-20200507235619393.png

image-20200507235605690.png

三、使用stop()方法(棄用)

爲何棄用stop:

  1. 調用 stop() 方法會馬上中止 run() 方法中剩餘的所有工做,包括在 catch 或 finally 語句中的,並拋出ThreadDeath異常(一般狀況下此異常不須要顯示的捕獲),所以可能會致使一些清理性的工做的得不到完成,如文件,數據庫等的關閉。
  2. 調用 stop() 方法會當即釋放該線程所持有的全部的鎖,致使數據得不到同步,出現數據不一致的問題。

線程優先度

  • Thread.setPriority(Integer num);

    線程優先度從1-10,可是仍然仍是CPU說的算

線程狀態監控

  • Thread.getState();
相關文章
相關標籤/搜索