欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java多線程ThreadAPI詳細(xì)介紹

 更新時(shí)間:2019年08月23日 14:53:47   作者:喵的波波魚  
這篇文章主要介紹了Java多線程ThreadAPI詳細(xì)介紹,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

1.Thread的構(gòu)造方法

package threadAPI;
 
public class CreateThread {
  public static void main(String[] args) {
    Thread t1 = new Thread();
    Thread t2 = new Thread();
 
    t1.start();
    t2.start();
 
    System.out.println(t1.getName());
    System.out.println(t2.getName());
 
  }
}

總結(jié)1:

創(chuàng)建線程對(duì)象Thread,默認(rèn)有一個(gè)線程名,以Thread-開頭,從0開始計(jì)數(shù)

Thread-0

Thread-1

Thread-2

可以看到Thread()中默認(rèn)傳入的第二個(gè)參數(shù),即Runnable接口為null

在init方法中,會(huì)將我們傳入的target給Thread的成員變量

然后在調(diào)用run方法的時(shí)候,會(huì)做如下判斷

所以當(dāng)target為null的時(shí)候,默認(rèn)的run方法中什么也不做

總結(jié)2:

如果在構(gòu)造Thread的時(shí)候,沒有傳遞Runnable接口或者沒有復(fù)寫Thread的run方法,該Thread將不會(huì)調(diào)用任何東西

如果傳遞了Runnable接口的實(shí)例,則會(huì)執(zhí)行該方法的邏輯代碼

如果復(fù)寫了Thread的run方法,則會(huì)執(zhí)行復(fù)寫的邏輯代碼

為線程傳遞一個(gè)線程名

這時(shí)我們傳入的參數(shù)名,會(huì)傳遞給線程對(duì)象的成員變量name

為線程傳遞線程名的同時(shí),傳遞Runnbale接口的實(shí)現(xiàn)類對(duì)象,調(diào)用原理同上

我們還可以在為線程傳入線程組

其實(shí)在上述的方法中沒有傳入線程組的情況下,init方法的ThreadGroup默認(rèn)被傳入null

parent即調(diào)用Thread對(duì)象的start方法的線程

package threadAPI;
 
public class CreateThread {
  public static void main(String[] args) {
    Thread t = new Thread();
    t.start();
 
    System.out.println(t.getThreadGroup());
    System.out.println(Thread.currentThread().getName());
    System.out.println(Thread.currentThread().getThreadGroup());
 
  }
}

總結(jié):

如果構(gòu)造線程對(duì)象時(shí)未傳入ThreadGroup,Thread默認(rèn)會(huì)獲取父線程的ThreadGroup作為該線程的ThreadGroup,此時(shí)子線程和父線程在同一個(gè)ThreadGroup中

我們可以查看當(dāng)前ThreadGroup中有多少個(gè)線程在運(yùn)行

package threadAPI;
 
public class CreateThread {
  public static void main(String[] args) {
    Thread t = new Thread();
    t.start();
 
    ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
    System.out.println(threadGroup.activeCount());
    
    //創(chuàng)建一個(gè)Thread數(shù)組
    Thread[] threads = new Thread[threadGroup.activeCount()];
    //將threadGroup中的數(shù)組枚舉到threads數(shù)組中
    threadGroup.enumerate(threads);
 
    //打印threads接收到的線程
    for (Thread thread : threads) {
      System.out.println(thread);
    }
  }
}

線程組的詳細(xì)介紹見后續(xù)博文:

stackSize:

演示代碼:

public class CreateThreadDemo {
 
  private static int counter;
 
  public static void main(String[] args) {
    Thread t1 = new Thread(null, new Runnable() {
      @Override
      public void run() {
        try {
          add(1);
        }catch (Error e){
          System.out.println(counter);
          e.printStackTrace();
        }
      }
 
      private void add(int i){
        counter++;
        add(i+1);
      }
 
    },"",1<<24);
 
    t1.start();
  }
}

運(yùn)行結(jié)果:

將stackSize修改:

修改為

運(yùn)行結(jié)果:

總結(jié):

構(gòu)造Thread的時(shí)候傳入stacksize代表著該線程占用虛擬機(jī)棧的大小(虛擬機(jī)棧本身的大小在程序運(yùn)行時(shí)就已經(jīng)確定),如果沒有指定stacksize的大小,默認(rèn)是0,0代表著會(huì)忽略該參數(shù),該參數(shù)會(huì)被JNI函數(shù)去使用

需要注意的是:該參數(shù)有一些平臺(tái)有效,在有些平臺(tái)則無(wú)效2.start方法

2、調(diào)用start

方法,會(huì)執(zhí)行run方法

但不能調(diào)用start方法兩次,會(huì)拋出異常

也可以直接調(diào)用Thread的run方法,但不會(huì)啟動(dòng)另一個(gè)線程

當(dāng)你第一次調(diào)用線程的start方法時(shí)候,會(huì)返回兩個(gè)線程,一個(gè)是調(diào)用線程,一個(gè)是新創(chuàng)建的執(zhí)行run方法的線程

start方法的源碼實(shí)現(xiàn)使用了模板方法,以下是模擬它的實(shí)現(xiàn)技巧:

public class TemplateMethod {
  //此處final,是因?yàn)閟tart方法的邏輯是固定的,不允許子類重寫此方法
  public final void start(String message)
  {
    System.out.println("################");
    run(message);
    System.out.println("################");
  }
 
  protected void run(String message)
  {
 
  }
 
  public static void main(String[] args) {
    TemplateMethod t1 = new TemplateMethod() {
      @Override
      protected void run(String message) {
        System.out.println("*"+message+"*");
      }
    };
 
    t1.start("hello,world");
 
    TemplateMethod t2 = new TemplateMethod() {
      @Override
      protected void run(String message) {
        System.out.println("+"+message+"+");
      }
    };
 
    t2.start("hello,world");
  }
}

上述代碼使用模板方法的大致思想是定義一個(gè)模板方法,它其中一些代碼已經(jīng)實(shí)現(xiàn),而另一些需要交給用戶去上實(shí)現(xiàn),該方法確定結(jié)構(gòu),對(duì)外提供統(tǒng)一的調(diào)用接口start,定義另一個(gè)方法,提供給用戶繼承,用戶可以重寫,也可以不重寫run方法,即不變的部分用模板實(shí)現(xiàn),變化的部分提取出來,交給用戶繼承重寫

3.setDaemon

Java中的線程分為兩種:

  • 用戶線程(setDaemo(false))
  • 守護(hù)線程(setDaemo(true))

什么是守護(hù)線程?

專門用于服務(wù)其他的線程,如果其他的線程(即用戶線程(包括main線程))都執(zhí)行完畢,JVM中只剩下守護(hù)線程時(shí),此時(shí)JVM不管守護(hù)線程是否執(zhí)行完畢,都會(huì)結(jié)束執(zhí)行

示例代碼1:

public class DaemonDemo {
  public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread(){
      @Override
      public void run() {
        try {
          System.out.println(Thread.currentThread().getName()+"\trun");
          Thread.sleep(10*1000);
          System.out.println(Thread.currentThread().getName()+"\tout");
        }catch (Exception e){
          e.printStackTrace();
        }
      }
    };
 
    t.setDaemon(true);
    t.start();
 
    Thread.sleep(5*1000);
 
    System.out.println(Thread.currentThread().getName());
  }
}

當(dāng)main線程執(zhí)行結(jié)束時(shí),運(yùn)行的唯一線程是守護(hù)線程,JVM退出,并未執(zhí)行此句

疑問:在我們創(chuàng)建的t線程中創(chuàng)建一個(gè)守護(hù)線程t1,當(dāng)t線程結(jié)束,主線程未結(jié)束的時(shí)候,守護(hù)線程是否結(jié)束呢?

示例代碼2:

/**
 * t1、t3、main是用戶線程
 * t2、t4是守護(hù)線程
 *
 * t1執(zhí)行1秒結(jié)束
 * main執(zhí)行5秒結(jié)束
 * t3執(zhí)行8秒結(jié)束
 *
 * t2、t4正常執(zhí)行完需要10秒
 */
public class DaemonDemo {
  public static void main(String[] args) throws InterruptedException {
 
    //t1線程睡眠1秒就執(zhí)行結(jié)束
    Thread t1 = new Thread(){
      @Override
      public void run() {
 
        //t2線程是守護(hù)線程,如果像用戶線程一樣,必須執(zhí)行10秒才執(zhí)行結(jié)束
        Thread t2 = new Thread(()-> {
          try {
            for (int i = 1; i <= 10; i++) {
              Thread.sleep(1*1000);
              System.out.println("t2守護(hù)線程執(zhí)行:"+i);
            }
 
 
            System.out.println("t2守護(hù)線程結(jié)束");
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        });
 
        t2.setDaemon(true);
        t2.start();
 
 
        try {
          Thread.sleep(1*1000);
          System.out.println("t1線程結(jié)束");
        }catch (Exception e){
          e.printStackTrace();
        }
      }
    };
 
    t1.start();
 
 
    //t3線程執(zhí)行8秒執(zhí)行結(jié)束
    Thread t3 = new Thread(()-> {
      try {
        for (int i = 1; i <= 8; i++) {
          Thread.sleep(1*1000);
          System.out.println("t3守護(hù)線程執(zhí)行:"+i);
        }
 
 
        System.out.println("t3守護(hù)線程結(jié)束");
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    });
 
    t3.start();
 
 
    //t4線程是守護(hù)線程,如果像用戶線程一樣,必須執(zhí)行10秒才執(zhí)行結(jié)束
    Thread t4 = new Thread(()-> {
      try {
        for (int i = 1; i <= 10; i++) {
          Thread.sleep(1*1000);
          System.out.println("t4守護(hù)線程執(zhí)行:"+i);
        }
 
 
        System.out.println("t4守護(hù)線程結(jié)束");
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    });
 
    t4.setDaemon(true);
    t4.start();
 
    //main線程執(zhí)行5秒就執(zhí)行結(jié)束
    Thread.sleep(5*1000);
 
    System.out.println("main線程結(jié)束");
  }
}

總結(jié):只有當(dāng)JVM中的用戶線程(包括main線程)執(zhí)行完的時(shí)候,未執(zhí)行完的守護(hù)線程會(huì)隨著JVM退出而被強(qiáng)制退出,不會(huì)再執(zhí)行后續(xù)的代碼

示例代碼3:

public class DaemonDemo {
  public static void main(String[] args) throws InterruptedException {
 
    Thread t1 = new Thread(){
      @Override
      public void run() {
 
        try {
          Thread.sleep(1*1000);
          System.out.println("t1線程結(jié)束");
        }catch (Exception e){
          e.printStackTrace();
        }
      }
    };
 
    t1.start();
    t1.setDaemon(true);
  }
}

總結(jié):

setDaemon(true)方法必須在調(diào)用start方法之前調(diào)用,

否則會(huì)拋出java.langIllegalThreadStateException異常

4.獲取線程的名稱、id、優(yōu)先級(jí)

(1)獲取線程的名稱

1.使用Thread類中的方法getName    

2.可以獲得當(dāng)前正在執(zhí)行的線程,使用線程中的方法getName()獲取線程的名稱    

示例代碼:

MyThread.java

package Demo01;
 
//1.創(chuàng)建一個(gè)Thread類的子類
public class MyThread extends Thread{
 
 
  //2.在Thread類的子類中重寫Thread類的run方法,設(shè)置線程任務(wù),即線程要干什么
  @Override
  public void run() {
    //第一種方式:直接調(diào)用getName獲取線程名稱
    System.out.println(this.getName());
 
    //第二種方式:獲得當(dāng)前正在執(zhí)行的線程,使用線程中的方法getName()獲取線程的名稱
    //System.out.println(Thread.currentThread().getName());
  }
}

ThreadDemo01.java

package Demo01;
 
public class ThreadDemo01 {
  public static void main(String[] args) {
 
    //3.創(chuàng)建Thread類的子類對(duì)象
    MyThread mt=new MyThread();
    //4.調(diào)用Thread類的start方法,開啟線程,執(zhí)行run方法
    mt.start();
 
    new MyThread().start();
    new MyThread().start();
    //獲得當(dāng)前正在執(zhí)行的線程,使用線程中的方法getName()獲取線程的名稱
    System.out.println(Thread.currentThread().getName());
  }
}

(2)獲取id

public class IdDemo {
  public static void main(String[] args) throws InterruptedException {
 
    Thread t1 = new Thread(()->{
 
      try {
        Thread.sleep(100*1000);
      }catch (Exception e){
        e.printStackTrace();
      }
 
 
    },"t1");
 
    System.out.println(t1.getName()+"線程的線程id為:"+t1.getId());
    System.out.println("main線程的id為:"+Thread.currentThread().getId());
  }
}

在JVM啟動(dòng)時(shí),除過main線程,JVM還會(huì)啟動(dòng)的9個(gè)后臺(tái)線程,我們的線程id從11開始遞增

(3)獲取線程優(yōu)先級(jí)

說明:高優(yōu)先級(jí)的線程要搶占低優(yōu)先級(jí)線程CPU的執(zhí)行權(quán),但是只是從概率上講,高優(yōu)先級(jí)的線程高概率的情況下被執(zhí)行,并不意味著只有當(dāng)高優(yōu)先級(jí)的線程執(zhí)行完以后,低優(yōu)先級(jí)的線程才執(zhí)行

通過優(yōu)先級(jí)可以企圖改變線程執(zhí)行的優(yōu)先順序,但是不一定會(huì)按照我們定義的順序去執(zhí)行,所以不要通過線程優(yōu)先級(jí)去控制先去執(zhí)行哪個(gè)線程再去執(zhí)行哪個(gè)線程

示例代碼:

public class PriorityDemo {
  public static void main(String[] args) throws InterruptedException {
 
    Thread t1 = new Thread(()->{
      for (int i = 0; i < 1000; i++) {
        System.out.println(Thread.currentThread().getName()+"-Index"+i);
      }
    },"t1");
 
    t1.setPriority(Thread.MAX_PRIORITY);
 
    Thread t2 = new Thread(()->{
      for (int i = 0; i < 1000; i++) {
        System.out.println(Thread.currentThread().getName()+"-Index"+i);
      }
    },"t2");
    t2.setPriority(Thread.NORM_PRIORITY);
 
    Thread t3 = new Thread(()->{
      for (int i = 0; i < 1000; i++) {
        System.out.println(Thread.currentThread().getName()+"-Index"+i);
      }
    },"t3");
    t3.setPriority(Thread.MIN_PRIORITY);
 
    t1.start();
    t2.start();
    t3.start();
 
  }
}

可以看到還是可以看到它們的交替執(zhí)行,并非一定按照我們賦予的優(yōu)先級(jí)順序來執(zhí)行

5.join

含義:調(diào)用join方法的線程等待執(zhí)行join方法的線程執(zhí)行結(jié)束(下面的方法是執(zhí)行join方法的線程固定時(shí)間,然后再繼續(xù)與存活的其他線程一起交替執(zhí)行下面的代碼)

示例代碼1:

當(dāng)沒有join之前

import java.util.stream.IntStream;
 
public class ThreadJoin {
  public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(()->{
      IntStream.range(1,10)
          .forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i));
    },"t1");
 
    t1.start();
 
    IntStream.range(1,10)
        .forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i));
 
 
  }
}

main線程和t1線程交替執(zhí)行

當(dāng)在main線程中調(diào)用t1線程的join方法時(shí)

import java.util.stream.IntStream;
 
public class ThreadJoin {
  public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(()->{
      IntStream.range(1,10)
          .forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i));
    },"t1");
 
    t1.start();
    t1.join();
 
    IntStream.range(1,10)
        .forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i));
 
 
  }
}

main線程要等到t1線程執(zhí)行完,再執(zhí)行它自己的代碼

示例代碼2:

import java.util.stream.IntStream;
 
public class ThreadJoin {
  public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(()->{
      IntStream.range(1,10)
          .forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i));
    },"t1");
 
 
    Thread t2 = new Thread(()->{
      IntStream.range(1,10)
          .forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i));
    },"t2");
 
    t1.start();
    t2.start();
    t1.join();
    t2.join();
 
    IntStream.range(1,10)
        .forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i));
 
  }
}

在A線程中調(diào)用B線程的join方法,則A等待B線程,與其他線程無(wú)關(guān)

上述代碼中,在main線程中調(diào)用了t1和t2的join方法,所以是main線程等t1和t2執(zhí)行完了,才繼續(xù)執(zhí)行下面的代碼,但是t1和t2線程之間沒有仍然交替執(zhí)行

示例代碼3:

import java.util.stream.IntStream;
 
public class ThreadJoin {
  public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(()->{
      try {
        Thread.sleep(1000);
        IntStream.range(1,10)
            .forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i));
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
 
 
    },"t1");
    
    t1.start();
    t1.join(1000);
 
 
    IntStream.range(1,10)
        .forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i));
    
  }
}

上述代碼main線程等待了t1線程執(zhí)行了1秒之后,又繼續(xù)與t1線程交替執(zhí)行即并發(fā)執(zhí)行

6.Interrupt

6.1 interrupt()和isInterrupted

其作用是中斷此線程(此線程不一定是當(dāng)前線程,而是指調(diào)用該方法的Thread實(shí)例所代表的線程),但實(shí)際上只是給線程設(shè)置一個(gè)中斷標(biāo)志,線程仍會(huì)繼續(xù)運(yùn)行。

而當(dāng)調(diào)用wait、sleep、join方法時(shí),就會(huì)清除該中斷標(biāo)志,并且拋出InterruptedException異常,我們可以捕獲該異常,然后做一些事情,比如,break跳出循環(huán)繼續(xù)向下執(zhí)行結(jié)束程序

只有一個(gè)作用:判斷此線程(此線程不一定是當(dāng)前線程,而是指調(diào)用該方法的Thread實(shí)例所代表的線程)的線程狀態(tài)(即中斷標(biāo)志是否被設(shè)置)

不會(huì)清除中斷標(biāo)志

示例代碼1:

public class InterruptDemo {
  public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread(){
      @Override
      public void run() {
        while(true){
          System.out.println("t線程正在執(zhí)行");
          System.out.println(">>"+this.isInterrupted());
          if(this.isInterrupted()){
            break;
          }
        }
 
        System.out.println("只是設(shè)置一個(gè)中斷標(biāo)志");
        System.out.println("中斷標(biāo)志還在否?"+this.isInterrupted());
      }
    };
 
    t.start();
    //簡(jiǎn)單進(jìn)行休眠,保證線程t進(jìn)入運(yùn)行狀態(tài)
    Thread.sleep(100);
    System.out.println("main\t"+t.isInterrupted());
    t.interrupt();
    System.out.println("main\t"+t.isInterrupted());
  }
}

結(jié)論:

  • 即使我們調(diào)用了interrupt,它并不會(huì)結(jié)束程序,而是設(shè)置一個(gè)中斷標(biāo)志
  • isInterrupted方法不會(huì)清除中斷標(biāo)志

示例代碼2:

public class InterruptDemo {
  public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread(){
      @Override
      public void run() {
        while(true){
          try {
            Thread.sleep(10);
          }catch (InterruptedException e){
            System.out.println("收到中斷信號(hào)");
            e.printStackTrace();
          }
        }
      }
    };
 
    t.start();
    //簡(jiǎn)單進(jìn)行休眠,保證線程t進(jìn)入運(yùn)行狀態(tài)
    Thread.sleep(100);
    System.out.println("main\t"+t.isInterrupted());
    t.interrupt();
    System.out.println("main\t"+t.isInterrupted());
  }
}

此種結(jié)果下程序是按照如下順序被調(diào)度的:

此種結(jié)果下程序是按照如下順序被調(diào)度的:

結(jié)論:

sleep方法會(huì)清除中斷標(biāo)志并拋出InterruptedException異常

示例代碼3:

public class InterruptDemo {
 
  private static final Object MONITTOR = new Object();
 
  public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread(){
      @Override
      public void run() {
        while(true){
 
          synchronized (MONITTOR){
            try {
              MONITTOR.wait(10);
            }catch (InterruptedException e){
              System.out.println("收到中斷信號(hào)");
              e.printStackTrace();
            }
          }
 
        }
      }
    };
 
    t.start();
    //簡(jiǎn)單進(jìn)行休眠,保證線程t進(jìn)入運(yùn)行狀態(tài)
    Thread.sleep(100);
    System.out.println("main\t"+t.isInterrupted());
    t.interrupt();
    System.out.println("main\t"+t.isInterrupted());
  }
}

結(jié)論:

wait方法會(huì)清除中斷標(biāo)志并拋出InterruptedException異常

示例代碼4:

public class InterruptDemo {
 
  public static void main(String[] args) {
    Thread t = new Thread(){
      @Override
      public void run() {
        while(true){
 
        }
      }
    };
 
    t.start();
 
 
    //使用t2線程去執(zhí)行interrupt()線程t
    Thread t2 = new Thread(){
      @Override
      public void run() {
        try {
          Thread.sleep(100);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
 
        t.interrupt();
        System.out.println("interrupt");
      }
    };
 
    t2.start();
 
    try {
      t.join();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

當(dāng)我們調(diào)用了join方法時(shí),使用interrupt方法設(shè)置線程標(biāo)志,我們發(fā)現(xiàn)程序并沒有被打斷,拋出異常,與API描述的不符?

我們反思一下,sleep,wait都是當(dāng)前線程sleep,wait,而這里調(diào)用join的是main線程,即main線程join,而我們中斷的是t線程,所以沒有拋出異常,代碼修改如下:

public class InterruptDemo {
 
  public static void main(String[] args) {
    Thread t = new Thread(){
      @Override
      public void run() {
        while(true){
 
        }
      }
    };
 
    t.start();
 
    Thread main = Thread.currentThread();
    //使用t2線程去執(zhí)行interrupt()線程t
    Thread t2 = new Thread(){
      @Override
      public void run() {
        try {
          Thread.sleep(100);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
 
        main.interrupt();
        System.out.println("interrupt");
      }
    };
 
    t2.start();
 
    try {
      t.join();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

可以看到j(luò)oin的底層還是在調(diào)用wait方法

6.2 interrupted()

兩個(gè)作用:

判斷當(dāng)前線程狀態(tài)(即中斷標(biāo)志是否被設(shè)置)

清除中斷標(biāo)志(如果已經(jīng)被設(shè)置)

  • 如果線程被interrupt()方法設(shè)置過中斷標(biāo)志,當(dāng)執(zhí)行interrupted()方法就會(huì)清除該中斷標(biāo)志并返回true,就相當(dāng)于我們接收到了interrupt()方法傳來的“中斷信號(hào)”,我們可以通過true或false這樣的判斷,來做一些事情
  • 如果線程中的中斷標(biāo)志沒有被設(shè)置,它就會(huì)返回false

有了isInterrupted()方法,為什么還要interrupted()方法呢?

因?yàn)槿绻覀兪莻鹘oThread一個(gè)Runnabe接口,重寫其中的run方法,我們又想調(diào)用判斷是否有中斷標(biāo)志,我們又無(wú)法調(diào)用isInterrupted()方法,所以Thread提供靜態(tài)方法interrupted()供我們使用

并且isInterrupted和interrupted一個(gè)很大區(qū)別就是:interrupted會(huì)清除中斷標(biāo)志,而isInterrupted不會(huì)

源碼如下:

供我們調(diào)用的isInterrupted:

本地方法isInterrupted:

供我們調(diào)用的靜態(tài)interrupted:

public class InterruptDemo {
 
  public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread(new Runnable() {
      @Override
      public void run() {
        {
          while(true){
 
            //此處在Runnable接口中重寫run方法,只能調(diào)用靜態(tài)方法interrupted判斷是否有中斷標(biāo)志
            if(Thread.interrupted())
            {
              System.out.println("收到中斷信號(hào)");
              System.out.println("中斷標(biāo)志還在否?"+Thread.interrupted());
              break;
            }
          }
        }
      }
    });
 
    t.start();
    //簡(jiǎn)單進(jìn)行休眠,保證線程t進(jìn)入運(yùn)行狀態(tài)
    Thread.sleep(100);
    System.out.println("main\t"+t.isInterrupted());
    t.interrupt();
    System.out.println("main\t"+t.isInterrupted());
  }
}

6.3 如何采用優(yōu)雅的方式結(jié)束線程?

(1)方式1:通過開關(guān)的方式即一個(gè)flag的方式去結(jié)束一個(gè)線程

/**
 * 通過開關(guān)的方式即一個(gè)flag的方式去結(jié)束一個(gè)線程
 */
public class ThreadCloseGraceful {
 
  private static class Worker extends Thread{
    private volatile boolean on = true;
 
    @Override
    public void run() {
      while(on){
 
      }
    }
 
 
    public void shutdown(){
      on = false;
    }
  }
 
  public static void main(String[] args) {
    Worker worker = new Worker();
    worker.start();
 
    try {
      Thread.sleep(10000);
    }catch (InterruptedException e){
      e.printStackTrace();
    }
 
    worker.shutdown();
  }
  
}

(2)方式2:通過中斷結(jié)束一個(gè)線程

/**
 * 通過捕獲中斷異常來結(jié)束程序
 */
public class ThreadCloseGraceful {
 
  private static class Worker extends Thread{
 
 
    @Override
    public void run() {
      while(true){
        try {
          Thread.sleep(1000); //遇到sleep清除中斷狀態(tài),并捕獲拋出的中斷異常
        }catch (InterruptedException e){
          System.out.println("Worker線程捕獲到了中斷異常");
          break;
        }
      }
      //這里還可以實(shí)現(xiàn)一些邏輯的代碼
 
      System.out.println("Worker線程被中斷結(jié)束");
    }
 
  }
 
  public static void main(String[] args) {
    Worker worker = new Worker();
    worker.start();
 
    try {
      Thread.sleep(5000);
    }catch (InterruptedException e){
      e.printStackTrace();
    }
 
    worker.interrupt(); //設(shè)置worker線程的中斷狀態(tài)
    System.out.println("主線程結(jié)束");
  }
 
}

/**
 * 通過線程調(diào)用靜態(tài)方法interrupted來結(jié)束程序
 */
public class ThreadCloseGraceful {
 
  private static class Worker extends Thread{
 
 
    @Override
    public void run() {
      while(true){
        if(Thread.interrupted()) //通過調(diào)用此方法,清除中斷狀態(tài),清除后返回true
          break;
        
      }
      //這里還可以實(shí)現(xiàn)一些邏輯的代碼
 
      System.out.println("Worker線程被中斷結(jié)束");
    }
 
  }
 
  public static void main(String[] args) {
    Worker worker = new Worker();
    worker.start();
 
    try {
      Thread.sleep(5000);
    }catch (InterruptedException e){
      e.printStackTrace();
    }
 
    worker.interrupt(); //設(shè)置中斷狀態(tài)
    System.out.println("主線程結(jié)束");
  }
 
}

(3)方式3:封裝一個(gè)類按照時(shí)間強(qiáng)制結(jié)束一個(gè)線程

分析上述兩種方式不能解決的問題:

當(dāng)我有一個(gè)很耗時(shí)的任務(wù),本來預(yù)期半個(gè)小時(shí)結(jié)束,結(jié)果它運(yùn)行了兩個(gè)小時(shí),我想結(jié)束它,也就是它在一次循環(huán)中一直執(zhí)行或阻塞,它沒有機(jī)會(huì)去判斷開關(guān)狀態(tài)或者中斷標(biāo)志狀態(tài)

方式3的代碼:

public class ThreadService {
 
  //定義一個(gè)執(zhí)行線程用于控制守護(hù)線程執(zhí)行我們的業(yè)務(wù)
  private Thread excuteThread;
  //用于判斷線程是否執(zhí)行完,執(zhí)行完的話,關(guān)閉方法就不用再去調(diào)用中斷方法
  private boolean finished = false;
 
  public void excute(Runnable task){
 
 
 
    excuteThread = new Thread(){
      @Override
      public void run() {
        //創(chuàng)建一個(gè)守護(hù)線程
        Thread runner = new Thread(task);
        runner.setDaemon(true);
        runner.start();
        try {
          runner.join();
          finished = true;
        } catch (InterruptedException e) {
 
        }
 
      }
    };
 
    excuteThread.start();
  }
 
  public void shutdown(long mills){
    long currentTime = System.currentTimeMillis();
    while (!finished){
      if(System.currentTimeMillis() - currentTime > mills){
        System.out.println("任務(wù)超時(shí),需要結(jié)束它");
        //我們執(zhí)行任務(wù)的線程由excuteThread線程去join
        // 那么我們?cè)O(shè)置中斷標(biāo)志,異常會(huì)被捕獲,然后不執(zhí)行任何代碼,直接結(jié)束執(zhí)行線程
        excuteThread.interrupt();
        break;
      }
 
      //業(yè)務(wù)既沒有完成,也沒有到我們?cè)O(shè)定的關(guān)閉時(shí)間,短暫的進(jìn)行休眠
      try {
        excuteThread.sleep(1);
      } catch (InterruptedException e) {
        System.out.println("執(zhí)行線程被打斷");
        break;
      }
    }
 
    finished = false;
  }
}
/**
 * 通過守護(hù)線程執(zhí)行業(yè)務(wù),通過一個(gè)用戶線程控制守護(hù)線程
 *
 * 注意:這種方法的前提是JVM中只有用戶線程,
 *    即當(dāng)我們調(diào)用shutdown以后不能再有其他用戶線程還在執(zhí)行,有的話,守護(hù)線程不會(huì)被結(jié)束
 */
public class ThreadCloseGraceful {
 
 
  public static void main(String[] args) {
 
    ThreadService service = new ThreadService();
    long start = System.currentTimeMillis();
    service.excute(()->{
 
      //假設(shè)在執(zhí)行一個(gè)很耗時(shí)的任務(wù)
      while(true){
 
        System.out.println("守護(hù)線程執(zhí)行的時(shí)間:"+(System.currentTimeMillis()-start)+"ms");
      }
    });
 
    //等待5000ms結(jié)束該線程
    service.shutdown(5000);
 
    long end = System.currentTimeMillis();
 
    System.out.println(end - start);
 
    /**
     * 此處如果不注釋,則為結(jié)果2
     *
     * 因?yàn)橹袛嘟Y(jié)束了執(zhí)行線程,但是main作為用戶線程并沒有結(jié)束,所以守護(hù)線程并沒有結(jié)束
     */
    /*
    try {
      Thread.sleep(10*1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    */
  }
 
}

運(yùn)行結(jié)果1:守護(hù)線程隨著唯一的excuteThread被結(jié)束而結(jié)束

運(yùn)行結(jié)果2:由于main線程在excuteThread結(jié)束后并未執(zhí)行完,守護(hù)線程未結(jié)束

所以注意:

這種方法的前提是JVM中只有一個(gè)用戶線程,即當(dāng)我們調(diào)用shutdown以后不能再有其他用戶線程還在執(zhí)行,有的話,守護(hù)線程不會(huì)被結(jié)束

7.yield

當(dāng)調(diào)用Thread.yield函數(shù)時(shí),會(huì)給線程調(diào)度器一個(gè)當(dāng)前線程愿意讓出CPU使用的暗示,只是大概率下會(huì)把CPU的執(zhí)行權(quán)交給其他線程,但是不是絕對(duì)的,線程調(diào)度器可能會(huì)忽略這個(gè)暗示,還可能再次把執(zhí)行權(quán)分配給當(dāng)前線程

yield也不會(huì)對(duì)鎖的行為有影響

public class YieldDemo {
  public static void main(String[] args) {
    Runnable yieldTask = new Runnable() {
      @Override
      public void run() {
        for (int i = 0; i < 10; i++) {
          System.out.println(Thread.currentThread().getName()+i);
          if(i == 5){
            Thread.yield();
          }
        }
      }
    };
 
    Thread t1 = new Thread(yieldTask,"A");
    Thread t2 = new Thread(yieldTask,"B");
    t1.start();
    t2.start();
  }
}

             

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • SpringBoot QQ郵箱發(fā)送郵件實(shí)例代碼

    SpringBoot QQ郵箱發(fā)送郵件實(shí)例代碼

    大家好,本篇文章主要講的是SpringBoot QQ郵箱發(fā)送郵件實(shí)例代碼,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • Spring Boot啟動(dòng)過程完全解析(一)

    Spring Boot啟動(dòng)過程完全解析(一)

    這篇文章主要介紹了Spring Boot啟動(dòng)過程完全解析(一),需要的朋友可以參考下
    2017-04-04
  • 利用Java讀取Word表格中文本和圖片的方法實(shí)例

    利用Java讀取Word表格中文本和圖片的方法實(shí)例

    這篇文章主要給大家介紹了關(guān)于如何利用Java讀取Word表格中文本和圖片的相關(guān)資料,主要利用的是free spire.doc.jar 包,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2021-07-07
  • Spring容器注入bean的五種方法逐個(gè)解析

    Spring容器注入bean的五種方法逐個(gè)解析

    依賴注入(Dependency Injection)和控制反轉(zhuǎn)(Inversion of Control)是同一個(gè)概念。具體含義是:當(dāng)某個(gè)角色(可能是一個(gè)Java實(shí)例,調(diào)用者)需要另一個(gè)角色(另一個(gè)Java實(shí)例,被調(diào)用者)的協(xié)助時(shí),在傳統(tǒng)的程序設(shè)計(jì)過程中,通常由調(diào)用者來創(chuàng)建被調(diào)用者的實(shí)例
    2023-02-02
  • springboot開啟Bean數(shù)據(jù)校驗(yàn)功能

    springboot開啟Bean數(shù)據(jù)校驗(yàn)功能

    這篇文章主要介紹了springboot開啟Bean數(shù)據(jù)校驗(yàn)功能,通過啟用Bean屬性校驗(yàn)導(dǎo)入JSR303與Hibernate校驗(yàn)框架坐標(biāo),使用@Validated注解啟用校驗(yàn)功能,需要的朋友可以參考下
    2023-10-10
  • Java線上CPU內(nèi)存沖高問題排查解決步驟

    Java線上CPU內(nèi)存沖高問題排查解決步驟

    這篇文章主要介紹了Java線上CPU內(nèi)存沖高問題排查解決步驟的相關(guān)資料,Java程序在實(shí)際生產(chǎn)過程中經(jīng)常遇到CPU或內(nèi)存使用率高的問題,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-07-07
  • java?spring?mvc處理器映射器介紹

    java?spring?mvc處理器映射器介紹

    這篇文章主要介紹了java?spring?mvc處理器映射器,文章圍繞equestMapping解析映射介紹展開源碼內(nèi)容,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-03-03
  • JAVA時(shí)間戳-Calendar類使用(包括set,get,add方法)

    JAVA時(shí)間戳-Calendar類使用(包括set,get,add方法)

    這篇文章主要介紹了JAVA時(shí)間戳-Calendar類使用(包括set,get,add方法),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • IDEA+maven+SpringBoot+JPA+Thymeleaf實(shí)現(xiàn)Crud及分頁(yè)

    IDEA+maven+SpringBoot+JPA+Thymeleaf實(shí)現(xiàn)Crud及分頁(yè)

    這篇文章主要介紹了不需要電腦任何操作基于IDEA + maven + SpringBoot + JPA + Thymeleaf實(shí)現(xiàn)CRUD及分頁(yè),需要的朋友可以參考下
    2018-03-03
  • SpringMVC ajax請(qǐng)求的處理方法介紹

    SpringMVC ajax請(qǐng)求的處理方法介紹

    Ajax即異步的 JavaScript和XML,是一種無(wú)需重新加載整個(gè)網(wǎng)頁(yè)的情況下,能夠更新部分模塊的網(wǎng)頁(yè)技術(shù),下面這篇文章主要給大家介紹了關(guān)于SpringMVC Ajax請(qǐng)求的處理,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-11-11

最新評(píng)論