Java多線程(上)

Java多線程

程序、進程和線程html

1、程序java

  • 程序是存儲在磁盤上, 包含可執行機器指令和數據的靜態實體。 即進程或者任務是處於活動狀態的計算機程序。

2、進程安全

  • 進程是資源(CPU、內存等)分配的基本單位,它是程序執行時的一個實例,即運行中的程序。網絡

  • 一個運行着的程序,可能有多個進程。進程在操做系統中執行特定的任務。多線程

  • 程序運行時系統就會建立一個進程,併爲它分配資源,而後把該進程放入進程就緒隊列,進程調度器選中它的時候就會爲它分配CPU時間,程序開始真正運行。ide

3、 線程函數

  • 線程就是程序的執行路線,即進程內部的控制序列,或者說是進程的子任務。
  • 線程,輕量級,不擁有本身獨立的內存資源,共享進程的代碼區、數據區、堆區(注意沒有棧區)、環境變量和命令行參數、文件描述符、信號處理函數、當前目錄、用戶ID和組ID等資源。
  • 線程擁有本身獨立的棧,所以也有本身獨立的局部變量。
  • 一個進程能夠同時擁有多個線程,即同時被系統調度的多條執行路線,但至少要有一個主線程。

線程實現

繼承Thread類

線程是程序中執行的線程。Java虛擬機容許應用程序同時運行多個執行線程。測試

每一個線程都有優先權。 具備較高優先級的線程優先於具備較低優先級的線程執行。 每一個線程可能也可能不會被標記爲守護進程。 當在某個線程中運行的代碼建立一個新的Thread對象時,新線程的優先級最初設置爲等於建立線程的優先級,而且當且僅當建立線程是守護進程時纔是守護進程線程。this

當Java虛擬機啓動時,一般會有一個非守護進程線程(一般調用某個指定類的名爲main的方法)。 Java虛擬機繼續執行線程,直到發生如下任一狀況:操作系統

  • 已調用類Runtimeexit方法,而且安全管理器已容許執行退出操做。
  • 經過調用run方法返回或拋出超出run方法傳播的異常,全部非守護程序線程的線程都已死亡。

建立線程方式一:繼承Thread類,重寫run()方法,調用start()開啓線程

public class MyThread extends Thread{
    @Override
    public void run() {
        //子線程方法
        for (int i = 0; i < 20; i++) {
            System.out.println("我是子線程" + i);
        }
    }

    public static void main(String[] args) {
        //開啓子線程
        new MyThread().start();
        
        for (int i = 0; i < 20; i++) {
            System.out.println("我是主線程" + i);
        }
    }
}

執行結果

能夠發現,主線程和子線程是」同時「進行的。

Thread類實現了Runnable接口,內部經過靜態代理調用了run()方法

實現Runnable接口

public class MyThread implements Runnable{
    @Override
    public void run() {
        //子線程方法
        for (int i = 0; i < 20; i++) {
            System.out.println("我是子線程" + i);
        }
    }

    public static void main(String[] args) {
        //建立線程對象,代理線程
        new Thread(new MyThread()).start();
        for (int i = 0; i < 20; i++) {
            System.out.println("我是主線程" + i);
        }
    }
}

使用方法基本和繼承Thread類相同,執行結果也類似。

實現Callable接口

  • 實現Callable接口,須要返回值類型
  • 重寫call方法,須要拋出異常
  • 建立目標對象
  • 建立執行服務: ExecutorService ser = Executors.newFixedThreadPool(1);
  • 提交執行: Future<Boolean> result1 = ser. submit(t1);
  • 獲取結果: boolean r1 = result1.get()
  • 關閉服務: ser. shutdownNow();

示例代碼

public class MyThread implements Callable<Boolean> {
    @Override
    public Boolean call() {
        //子線程方法
        System.out.println("執行了子線程");
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyThread t1 = new MyThread();
        //建立執行服務
        ExecutorService ser = Executors.newFixedThreadPool(1);
        //提交執行
        Future<Boolean> result1 = ser.submit(t1);
        //獲取結果
        boolean r1 = result1.get();
        //關閉服務
        ser. shutdownNow();
    }
}

線程狀態

線程五大狀態

  • 建立狀態
  • 就緒狀態
  • 阻塞狀態
  • 運行狀態
  • 死亡狀態

五個狀態的轉化


建立狀態:

Thread t = new Thread()

線程對象一旦建立就進入到了新生狀態。

就緒狀態:

當調用start()方法,線程當即進入就緒狀態,但不意味着當即調度執行。

運行狀態:

進入運行狀態,線程才真正執行線程體的代碼塊。

阻塞狀態:

當調用sleep, wait 或同步鎖定時,線程進入阻塞狀態,就是代碼不往下執行,阻塞事件解除後,從新進入就緒狀態,等待cpu調度執行。

不必定每一個線程都會進入阻塞狀態

死亡狀態:

線程中斷或者結束,一旦進入死亡狀態,就不能再次啓動。

方法 說明
setPriority(int newPriority) 更改線程的優先級
static void sleep(long millis) 在指定的毫秒數內讓當前正在執行的線程休眠
void join() 等待該線程終止
static void yield() 暫停當前正在執行的線程對象,並執行其餘線程
void interrupt() 中斷線程,別用這個方式
boolean isAlive() 測試線程是否處於活動狀態

線程中止

  • 開始線程可使用start()方法。
  • 建議線程正常中止,利用次數,不建議死循環。
  • 建議使用標誌位,設置一個標誌位。
  • 不要使用stop或者destroy等過期或者JDK不建議使用的方法。

使用標誌位的例子

public class MyThread implements Runnable{

    private boolean flag = true;

    @Override
    public void run() {
        //子線程方法
        int i = 0;
        while (flag) {
            System.out.println("我是子線程" + i++);
        }
    }

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

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        new Thread(thread).start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("主線程" + i);
            if (i == 800) {
                thread.stop();
                System.out.println("線程中止了");
            }
        }
    }
}

結果如圖

線程休眠

  • sleep (時間)指定當前線程阻塞的毫秒數
  • sleep存在異常InterruptedException
  • sleep時間達到後線程進入就緒狀態
  • sleep能夠模擬網絡延時,倒計時
  • 每個對象都有一-個鎖, sleep不會釋放鎖

線程休眠的例子:

public class MyThread{
    public static void main(String[] args) throws InterruptedException {
        while (true) {
            Date date = new Date(System.currentTimeMillis());
            System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
            Thread.sleep(1000);
        }
    }
}

每一秒輸出當前時間,使用sleep(1000)來使得每次執行完休眠一秒。

線程禮讓

  • 禮讓線程,讓當前正在執行的線程暫停,但不阻塞
  • 將線程從運行狀態轉爲就緒狀態
  • 讓cpu從新調度,禮讓不-定成功!看CPU心情

示例

public class MyThread{
    public static void main(String[] args) {
        Runnable runnable = () -> {
            System.out.println(Thread.currentThread().getName() + "線程開始");
            Thread.yield(); //線程禮讓
            System.out.println(Thread.currentThread().getName() + "線程結束");
        };

        new Thread(runnable, "a").start();
        new Thread(runnable, "b").start();
        new Thread(runnable, "c").start();
    }
}

執行結果

能夠發現每次的結果都不相同

線程聯合

能夠想象成一個VIP線程,強制插隊,查到當前線程,直到本身執行完畢。很是霸道

會使得線程阻塞。

public class MyThread{
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("VIP線程來了" + i);
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();

        for (int i = 0; i < 50; i++) {
            if (i == 30) thread.join();
            System.out.println("主線程" + i);
        }
    }
}

結果如圖

線程狀態觀測

  • 線程狀態。線程能夠處於如下狀態之一:

    • NEW
      還沒有啓動的線程處於此狀態。
    • RUNNABLE
      在Java虛擬機中執行的線程處於此狀態。
    • BLOCKED
      被阻塞等待監視器鎖定的線程處於此狀態。
    • WAITING
      無限期等待另外一個線程執行特定操做的線程處於此狀態。
    • TIMED_WAITING
      正在等待另外一個線程執行最多指定等待時間的操做的線程處於此狀態。
    • TERMINATED
      已退出的線程處於此狀態。

    線程在給定時間點只能處於一種狀態。 這些狀態是虛擬機狀態,不反映任何操做系統線程狀態。

經過如下代碼演示線程狀態的監聽

public class MyThread{
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ignored) {}
            }

        });
        System.out.println(thread.getState());
        thread.start();
        while (thread.getState() != Thread.State.TERMINATED) {
            Thread.sleep(500);
            System.out.println(thread.getState());
        }
    }
}

運行結果

線程的優先級

Java提供一個線程調度器來監控程序中啓動後進入就緒狀態的全部線程, 線程調度
器按照優先級決定應該調度哪一個線程來執行。

觀察Thread類源代碼,能夠看到優先級在jdk中的定義

/**
  * The minimum priority that a thread can have.
  */
 public static final int MIN_PRIORITY = 1;

/**
  * The default priority that is assigned to a thread.
  */
 public static final int NORM_PRIORITY = 5;

 /**
  * The maximum priority that a thread can have.
  */
 public static final int MAX_PRIORITY = 10;
  • 線程的優先級用數字表示,範圍從1~10.
    • Thread.MIN_ PRIORITY= 1;
    • Thread.MAX_ PRIORITY= 10;
    • Thread.NORM_ PRIORITY = 5;

咱們能夠經過

System.out.println(Thread.currentThread().getPriority());

來輸出當前線程的優先級,能夠獲得默認優先級是5

可使用如下方式改變或獲取優先級

getPriority() //獲得優先級
setPriority(int xxx)  設置優先級

若是設置的優先級超出限制,則會拋出一個IllegalArgumentException異常:

if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
    throw new IllegalArgumentException();
}

優先級低只是意味着得到調度的機率低,並非優先級低就不會被調用了,這都是看CPU的調度。

最好在start前設置優先級

守護(daemon)線程

  • 線程分爲用戶線程守護線程
  • 虛擬機必須確保用戶線程執行完畢
  • 虛擬機不用等待守護線程執行完畢
  • 如,後臺記錄操做日誌,監控內存垃圾回收等待..

所謂守護線程,是指在程序運行的時候在後臺提供一種通用服務的線程,好比垃圾回收線程就是一個很稱職的守護者,而且這種線程並不屬於程序中不可或缺的部分。所以,當全部的非守護線程結束時,程序也就終止了,同時會殺死進程中的全部守護線程。反過來講,只要任何非守護線程還在運行,程序就不會終止。

用戶線程守護線程二者幾乎沒有區別,惟一的不一樣之處就在於虛擬機的離開:若是用戶線程已經所有退出運行了,只剩下守護線程存在了,虛擬機也就退出了。 由於沒有了被守護者,守護線程也就沒有工做可作了,也就沒有繼續運行程序的必要了。

演示,先定義兩個線程,一個守護線程,一個普通線程

Thread daemon = new Thread(()->{
    while (true) {
        System.out.println("守護線程...");
    }
});
Thread normal = new Thread(()->{
    for (int i = 0; i < 10; i++) {
        System.out.println("子線程...");
    }
    System.out.println("子線程結束");
});

而後設置守護線程

daemon.setDaemon(true);

啓動兩個線程後,能夠看到,守護線程並無結束就中止了程序。

Java多線程(下)https://www.cnblogs.com/chaofanq/p/15055853.html

相關文章
相關標籤/搜索