Java多线程ThreadAPI详细介绍

1.Thread的构造方法

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());

  }
}

总结1:

创建线程对象Thread,默认有一个线程名,以Thread-开头,从0开始计数

Thread-0

Thread-1

Thread-2

可以看到Thread()中默认传入的第二个参数,即Runnable接口为null

在init方法中,会将我们传入的target给Thread的成员变量

然后在调用run方法的时候,会做如下判断

所以当target为null的时候,默认的run方法中什么也不做

总结2:

如果在构造Thread的时候,没有传递Runnable接口或者没有复写Thread的run方法,该Thread将不会调用任何东西

如果传递了Runnable接口的实例,则会执行该方法的逻辑代码

如果复写了Thread的run方法,则会执行复写的逻辑代码

为线程传递一个线程名

这时我们传入的参数名,会传递给线程对象的成员变量name

为线程传递线程名的同时,传递Runnbale接口的实现类对象,调用原理同上

我们还可以在为线程传入线程组

其实在上述的方法中没有传入线程组的情况下,init方法的ThreadGroup默认被传入null

parent即调用Thread对象的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());

  }
}

总结:

如果构造线程对象时未传入ThreadGroup,Thread默认会获取父线程的ThreadGroup作为该线程的ThreadGroup,此时子线程和父线程在同一个ThreadGroup中

我们可以查看当前ThreadGroup中有多少个线程在运行

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());

    //创建一个Thread数组
    Thread[] threads = new Thread[threadGroup.activeCount()];
    //将threadGroup中的数组枚举到threads数组中
    threadGroup.enumerate(threads);

    //打印threads接收到的线程
    for (Thread thread : threads) {
      System.out.println(thread);
    }
  }
}

线程组的详细介绍见后续博文:

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();
  }
}

运行结果:

将stackSize修改:

修改为

运行结果:

总结:

构造Thread的时候传入stacksize代表着该线程占用虚拟机栈的大小(虚拟机栈本身的大小在程序运行时就已经确定),如果没有指定stacksize的大小,默认是0,0代表着会忽略该参数,该参数会被JNI函数去使用

需要注意的是:该参数有一些平台有效,在有些平台则无效2.start方法

2、调用start

方法,会执行run方法

但不能调用start方法两次,会抛出异常

也可以直接调用Thread的run方法,但不会启动另一个线程

当你第一次调用线程的start方法时候,会返回两个线程,一个是调用线程,一个是新创建的执行run方法的线程

start方法的源码实现使用了模板方法,以下是模拟它的实现技巧:

public class TemplateMethod {
  //此处final,是因为start方法的逻辑是固定的,不允许子类重写此方法
  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");
  }
}

上述代码使用模板方法的大致思想是定义一个模板方法,它其中一些代码已经实现,而另一些需要交给用户去上实现,该方法确定结构,对外提供统一的调用接口start,定义另一个方法,提供给用户继承,用户可以重写,也可以不重写run方法,即不变的部分用模板实现,变化的部分提取出来,交给用户继承重写

3.setDaemon

Java中的线程分为两种:

  • 用户线程(setDaemo(false))
  • 守护线程(setDaemo(true))

什么是守护线程?

专门用于服务其他的线程,如果其他的线程(即用户线程(包括main线程))都执行完毕,JVM中只剩下守护线程时,此时JVM不管守护线程是否执行完毕,都会结束执行

示例代码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());
  }
}

当main线程执行结束时,运行的唯一线程是守护线程,JVM退出,并未执行此句

疑问:在我们创建的t线程中创建一个守护线程t1,当t线程结束,主线程未结束的时候,守护线程是否结束呢?

示例代码2:

/**
 * t1、t3、main是用户线程
 * t2、t4是守护线程
 *
 * t1执行1秒结束
 * main执行5秒结束
 * t3执行8秒结束
 *
 * t2、t4正常执行完需要10秒
 */
public class DaemonDemo {
  public static void main(String[] args) throws InterruptedException {

    //t1线程睡眠1秒就执行结束
    Thread t1 = new Thread(){
      @Override
      public void run() {

        //t2线程是守护线程,如果像用户线程一样,必须执行10秒才执行结束
        Thread t2 = new Thread(()-> {
          try {
            for (int i = 1; i <= 10; i++) {
              Thread.sleep(1*1000);
              System.out.println("t2守护线程执行:"+i);
            }

            System.out.println("t2守护线程结束");
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        });

        t2.setDaemon(true);
        t2.start();

        try {
          Thread.sleep(1*1000);
          System.out.println("t1线程结束");
        }catch (Exception e){
          e.printStackTrace();
        }
      }
    };

    t1.start();

    //t3线程执行8秒执行结束
    Thread t3 = new Thread(()-> {
      try {
        for (int i = 1; i <= 8; i++) {
          Thread.sleep(1*1000);
          System.out.println("t3守护线程执行:"+i);
        }

        System.out.println("t3守护线程结束");
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    });

    t3.start();

    //t4线程是守护线程,如果像用户线程一样,必须执行10秒才执行结束
    Thread t4 = new Thread(()-> {
      try {
        for (int i = 1; i <= 10; i++) {
          Thread.sleep(1*1000);
          System.out.println("t4守护线程执行:"+i);
        }

        System.out.println("t4守护线程结束");
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    });

    t4.setDaemon(true);
    t4.start();

    //main线程执行5秒就执行结束
    Thread.sleep(5*1000);

    System.out.println("main线程结束");
  }
}

总结:只有当JVM中的用户线程(包括main线程)执行完的时候,未执行完的守护线程会随着JVM退出而被强制退出,不会再执行后续的代码

示例代码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线程结束");
        }catch (Exception e){
          e.printStackTrace();
        }
      }
    };

    t1.start();
    t1.setDaemon(true);
  }
}

总结:

setDaemon(true)方法必须在调用start方法之前调用,

否则会抛出java.langIllegalThreadStateException异常

4.获取线程的名称、id、优先级

(1)获取线程的名称

1.使用Thread类中的方法getName

2.可以获得当前正在执行的线程,使用线程中的方法getName()获取线程的名称

示例代码:

MyThread.java

package Demo01;

//1.创建一个Thread类的子类
public class MyThread extends Thread{

  //2.在Thread类的子类中重写Thread类的run方法,设置线程任务,即线程要干什么
  @Override
  public void run() {
    //第一种方式:直接调用getName获取线程名称
    System.out.println(this.getName());

    //第二种方式:获得当前正在执行的线程,使用线程中的方法getName()获取线程的名称
    //System.out.println(Thread.currentThread().getName());
  }
}

ThreadDemo01.java

package Demo01;

public class ThreadDemo01 {
  public static void main(String[] args) {

    //3.创建Thread类的子类对象
    MyThread mt=new MyThread();
    //4.调用Thread类的start方法,开启线程,执行run方法
    mt.start();

    new MyThread().start();
    new MyThread().start();
    //获得当前正在执行的线程,使用线程中的方法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启动时,除过main线程,JVM还会启动的9个后台线程,我们的线程id从11开始递增

(3)获取线程优先级

说明:高优先级的线程要抢占低优先级线程CPU的执行权,但是只是从概率上讲,高优先级的线程高概率的情况下被执行,并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行

通过优先级可以企图改变线程执行的优先顺序,但是不一定会按照我们定义的顺序去执行,所以不要通过线程优先级去控制先去执行哪个线程再去执行哪个线程

示例代码:

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();

  }
}

可以看到还是可以看到它们的交替执行,并非一定按照我们赋予的优先级顺序来执行

5.join

含义:调用join方法的线程等待执行join方法的线程执行结束(下面的方法是执行join方法的线程固定时间,然后再继续与存活的其他线程一起交替执行下面的代码)

示例代码1:

当没有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线程交替执行

当在main线程中调用t1线程的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();
    t1.join();

    IntStream.range(1,10)
        .forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i));

  }
}

main线程要等到t1线程执行完,再执行它自己的代码

示例代码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线程中调用B线程的join方法,则A等待B线程,与其他线程无关

上述代码中,在main线程中调用了t1和t2的join方法,所以是main线程等t1和t2执行完了,才继续执行下面的代码,但是t1和t2线程之间没有仍然交替执行

示例代码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线程执行了1秒之后,又继续与t1线程交替执行即并发执行

6.Interrupt

6.1 interrupt()和isInterrupted

其作用是中断此线程(此线程不一定是当前线程,而是指调用该方法的Thread实例所代表的线程),但实际上只是给线程设置一个中断标志,线程仍会继续运行。

而当调用wait、sleep、join方法时,就会清除该中断标志,并且抛出InterruptedException异常,我们可以捕获该异常,然后做一些事情,比如,break跳出循环继续向下执行结束程序

只有一个作用:判断此线程(此线程不一定是当前线程,而是指调用该方法的Thread实例所代表的线程)的线程状态(即中断标志是否被设置)

不会清除中断标志

示例代码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线程正在执行");
          System.out.println(">>"+this.isInterrupted());
          if(this.isInterrupted()){
            break;
          }
        }

        System.out.println("只是设置一个中断标志");
        System.out.println("中断标志还在否?"+this.isInterrupted());
      }
    };

    t.start();
    //简单进行休眠,保证线程t进入运行状态
    Thread.sleep(100);
    System.out.println("main\t"+t.isInterrupted());
    t.interrupt();
    System.out.println("main\t"+t.isInterrupted());
  }
}

结论:

  • 即使我们调用了interrupt,它并不会结束程序,而是设置一个中断标志
  • isInterrupted方法不会清除中断标志

示例代码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("收到中断信号");
            e.printStackTrace();
          }
        }
      }
    };

    t.start();
    //简单进行休眠,保证线程t进入运行状态
    Thread.sleep(100);
    System.out.println("main\t"+t.isInterrupted());
    t.interrupt();
    System.out.println("main\t"+t.isInterrupted());
  }
}

此种结果下程序是按照如下顺序被调度的:

此种结果下程序是按照如下顺序被调度的:

结论:

sleep方法会清除中断标志并抛出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("收到中断信号");
              e.printStackTrace();
            }
          }

        }
      }
    };

    t.start();
    //简单进行休眠,保证线程t进入运行状态
    Thread.sleep(100);
    System.out.println("main\t"+t.isInterrupted());
    t.interrupt();
    System.out.println("main\t"+t.isInterrupted());
  }
}

结论:

wait方法会清除中断标志并抛出InterruptedException异常

示例代码4:

public class InterruptDemo {

  public static void main(String[] args) {
    Thread t = new Thread(){
      @Override
      public void run() {
        while(true){

        }
      }
    };

    t.start();

    //使用t2线程去执行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();
    }
  }
}

当我们调用了join方法时,使用interrupt方法设置线程标志,我们发现程序并没有被打断,抛出异常,与API描述的不符?

我们反思一下,sleep,wait都是当前线程sleep,wait,而这里调用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线程去执行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();
    }
  }
}

可以看到join的底层还是在调用wait方法

6.2 interrupted()

两个作用:

判断当前线程状态(即中断标志是否被设置)

清除中断标志(如果已经被设置)

  • 如果线程被interrupt()方法设置过中断标志,当执行interrupted()方法就会清除该中断标志并返回true,就相当于我们接收到了interrupt()方法传来的“中断信号”,我们可以通过true或false这样的判断,来做一些事情
  • 如果线程中的中断标志没有被设置,它就会返回false

有了isInterrupted()方法,为什么还要interrupted()方法呢?

因为如果我们是传给Thread一个Runnabe接口,重写其中的run方法,我们又想调用判断是否有中断标志,我们又无法调用isInterrupted()方法,所以Thread提供静态方法interrupted()供我们使用

并且isInterrupted和interrupted一个很大区别就是:interrupted会清除中断标志,而isInterrupted不会

源码如下:

供我们调用的isInterrupted:

本地方法isInterrupted:

供我们调用的静态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方法,只能调用静态方法interrupted判断是否有中断标志
            if(Thread.interrupted())
            {
              System.out.println("收到中断信号");
              System.out.println("中断标志还在否?"+Thread.interrupted());
              break;
            }
          }
        }
      }
    });

    t.start();
    //简单进行休眠,保证线程t进入运行状态
    Thread.sleep(100);
    System.out.println("main\t"+t.isInterrupted());
    t.interrupt();
    System.out.println("main\t"+t.isInterrupted());
  }
}

6.3 如何采用优雅的方式结束线程?

(1)方式1:通过开关的方式即一个flag的方式去结束一个线程

/**
 * 通过开关的方式即一个flag的方式去结束一个线程
 */
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:通过中断结束一个线程

/**
 * 通过捕获中断异常来结束程序
 */
public class ThreadCloseGraceful {

  private static class Worker extends Thread{

    @Override
    public void run() {
      while(true){
        try {
          Thread.sleep(1000); //遇到sleep清除中断状态,并捕获抛出的中断异常
        }catch (InterruptedException e){
          System.out.println("Worker线程捕获到了中断异常");
          break;
        }
      }
      //这里还可以实现一些逻辑的代码

      System.out.println("Worker线程被中断结束");
    }

  }

  public static void main(String[] args) {
    Worker worker = new Worker();
    worker.start();

    try {
      Thread.sleep(5000);
    }catch (InterruptedException e){
      e.printStackTrace();
    }

    worker.interrupt(); //设置worker线程的中断状态
    System.out.println("主线程结束");
  }

}

/**
 * 通过线程调用静态方法interrupted来结束程序
 */
public class ThreadCloseGraceful {

  private static class Worker extends Thread{

    @Override
    public void run() {
      while(true){
        if(Thread.interrupted()) //通过调用此方法,清除中断状态,清除后返回true
          break;

      }
      //这里还可以实现一些逻辑的代码

      System.out.println("Worker线程被中断结束");
    }

  }

  public static void main(String[] args) {
    Worker worker = new Worker();
    worker.start();

    try {
      Thread.sleep(5000);
    }catch (InterruptedException e){
      e.printStackTrace();
    }

    worker.interrupt(); //设置中断状态
    System.out.println("主线程结束");
  }

}

(3)方式3:封装一个类按照时间强制结束一个线程

分析上述两种方式不能解决的问题:

当我有一个很耗时的任务,本来预期半个小时结束,结果它运行了两个小时,我想结束它,也就是它在一次循环中一直执行或阻塞,它没有机会去判断开关状态或者中断标志状态

方式3的代码:

public class ThreadService {

  //定义一个执行线程用于控制守护线程执行我们的业务
  private Thread excuteThread;
  //用于判断线程是否执行完,执行完的话,关闭方法就不用再去调用中断方法
  private boolean finished = false;

  public void excute(Runnable task){

    excuteThread = new Thread(){
      @Override
      public void run() {
        //创建一个守护线程
        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("任务超时,需要结束它");
        //我们执行任务的线程由excuteThread线程去join
        // 那么我们设置中断标志,异常会被捕获,然后不执行任何代码,直接结束执行线程
        excuteThread.interrupt();
        break;
      }

      //业务既没有完成,也没有到我们设定的关闭时间,短暂的进行休眠
      try {
        excuteThread.sleep(1);
      } catch (InterruptedException e) {
        System.out.println("执行线程被打断");
        break;
      }
    }

    finished = false;
  }
}
/**
 * 通过守护线程执行业务,通过一个用户线程控制守护线程
 *
 * 注意:这种方法的前提是JVM中只有用户线程,
 *    即当我们调用shutdown以后不能再有其他用户线程还在执行,有的话,守护线程不会被结束
 */
public class ThreadCloseGraceful {

  public static void main(String[] args) {

    ThreadService service = new ThreadService();
    long start = System.currentTimeMillis();
    service.excute(()->{

      //假设在执行一个很耗时的任务
      while(true){

        System.out.println("守护线程执行的时间:"+(System.currentTimeMillis()-start)+"ms");
      }
    });

    //等待5000ms结束该线程
    service.shutdown(5000);

    long end = System.currentTimeMillis();

    System.out.println(end - start);

    /**
     * 此处如果不注释,则为结果2
     *
     * 因为中断结束了执行线程,但是main作为用户线程并没有结束,所以守护线程并没有结束
     */
    /*
    try {
      Thread.sleep(10*1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    */
  }

}

运行结果1:守护线程随着唯一的excuteThread被结束而结束

运行结果2:由于main线程在excuteThread结束后并未执行完,守护线程未结束

所以注意:

这种方法的前提是JVM中只有一个用户线程,即当我们调用shutdown以后不能再有其他用户线程还在执行,有的话,守护线程不会被结束

7.yield

当调用Thread.yield函数时,会给线程调度器一个当前线程愿意让出CPU使用的暗示,只是大概率下会把CPU的执行权交给其他线程,但是不是绝对的,线程调度器可能会忽略这个暗示,还可能再次把执行权分配给当前线程

yield也不会对锁的行为有影响

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();
  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Java Thread多线程全面解析

    多线程是java中很重要的知识点,在此小编给大家总结Java Thread多线程,非常有用,希望大家可以掌握哦. 一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的图: 上图中基本上囊括了Java中多线程各重要知识点.掌握了上图中的各知识点,Java中的多线程也就基本上掌握了.主要包括: Java线程具有五种基本状态 新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread(); 就绪状态(Runnab

  • Java使用Thread创建多线程并启动操作示例

    本文实例讲述了Java使用Thread创建多线程并启动操作.分享给大家供大家参考,具体如下: 按照教程实现了一个单线程的创建,但是单线程的创建于启动并不是很有实用价值的.毕竟直接在main方法中放着相关的执行操作本身也就是一种单线程的实现.接下来在之前用过的代码基础上稍作修改,形成如下代码: class ThreadDemo extends Thread { ThreadDemo(){}; ThreadDemo(String szName) { super(szName); } public v

  • java Thread 多线程

    Thread 创建线程的两种方法: 1.定义类继承Thread类,覆写类中的run方法,调用类对象的start方法,start方法启动线程,调用run方法.Thread类用于描述线程:该类定义一个功能run,用于存储线程要运行的代码. 2.定义类实现Runnable接口,覆盖Runnable接口中的方法,通过Thread类建立线程对象,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数,调用Thread类的start方法开启线程,线程会调用Runnable接口子类中的ru

  • java 多线程Thread与runnable的区别

    java 多线程Thread与runnable的区别 java中实现多线程的方法有两种:继承Thread类和实现runnable接口 1,继承Thread类,重写父类run()方法 public class thread1 extends Thread { public void run() { for (int i = 0; i < 10000; i++) { System.out.println("我是线程"+this.getId()); } } public static

  • Java多线程继承Thread类详解第1/2页

    调用方法: /** * 点击量/月(年)Thread */ public void yearlyClickThread() { // 获取参数 String year = getPara("year"); // 统计数据集X List<String> xList = new ArrayList<String>(); xList.add("January"); xList.add("February"); xList.add

  • Java Thread多线程详解及用法解析

    最全面的java多线程用法解析,如果你对Java的多线程机制并没有深入的研究,那么本文可以帮助你更透彻地理解Java多线程的原理以及使用方法. 1.创建线程 在Java中创建线程有两种方法:使用Thread类和使用Runnable接口.在使用Runnable接口时需要建立一个Thread实例.因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或它的子类的实例.Thread构造函数: public Thread( ); public Thread(Runnab

  • java多线程编程之使用thread类创建线程

    在Java中创建线程有两种方法:使用Thread类和使用Runnable接口.在使用Runnable接口时需要建立一个Thread实例.因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或它的子类的实例.Thread类的构造方法被重载了八次,构造方法如下: 复制代码 代码如下: public Thread( );public Thread(Runnable target);public Thread(String name);public Thread(Ru

  • java多线程Thread的实现方法代码详解

    之前有简单介绍过java多线程的使用,已经Thread类和Runnable类,为了更好地理解多线程,本文就Thread进行详细的分析. start() 我们先来看看API中对于该方法的介绍: 使该线程开始执行:Java 虚拟机调用该线程的 run 方法. 结果是两个线程并发地运行:当前线程(从调用返回给 start 方法)和另一个线程(执行其 run 方法). 多次启动一个线程是非法的.特别是当线程已经结束执行后,不能再重新启动. 用start方法来启动线程,真正实现了多线程运行,这时无需等待r

  • Java多线程ThreadAPI详细介绍

    1.Thread的构造方法 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()); } }

  • Java多线程atomic包介绍及使用方法

    引言 Java从JDK1.5开始提供了java.util.concurrent.atomic包,方便程序员在多线程环境下,无锁的进行原子操作.原子变量的底层使用了处理器提供的原子指令,但是不同的CPU架构可能提供的原子指令不一样,也有可能需要某种形式的内部锁,所以该方法不能绝对保证线程不被阻塞. Atomic包介绍 在Atomic包里一共有12个类,四种原子更新方式,分别是原子更新基本类型,原子更新数组,原子更新引用和原子更新字段.Atomic包里的类基本都是使用Unsafe实现的包装类. 原子

  • Java操作Redis详细介绍

    1. 简介 Redis 是一个开源(BSD许可)的,内存中的key-value存储系统,它可以用作数据库.缓存和消息中间件. 2. 对key的操作 首先要建立连接Jedis jedis = new Jedis("127.0.0.1", 6379),然后就可以对string,set,zset,hash进行操作了. //对key的测试 public void keyTest() { System.out.println(jedis.flushDB()); //清空数据 System.out

  • Java多线程的具体介绍与使用笔记小结

    一.基本概念:线程.进程 1.1.进程与线程的具体介绍 线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径. 若一个进程同一时间并行执行多个线程,就是支持多线程的 线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小 一个进程中的多个线程共享相同的内存单元/内存地址空间à它们从同一堆中分配对象,可以访问相同的变量和对象.这就使得线程间通信更简便.高效.但多个线程操作共享的系统资源可能就会带来安全的隐患. 进程(process),是程序的

  • Java InheritableThreadLocal用法详细介绍

    目录 简介 问题复现 解决方案 源码分析 注意 简介 本文介绍InheritableThreadLocal的用法. ThreadLocal可以将数据绑定当前线程,如果希望当前线程的ThreadLocal的数据被子线程使用,实现方式就会相当困难(需要用户自己在代码中传递). InheritableThreadLocal可以方便地让子线程自动获取父线程ThreadLocal的数据. ThreadLocal和InheritableThreadLocal都要注意,用完后要调用其remove()方法,不然

  • java枚举使用详细介绍及实现

    java枚举使用详解 在实际编程中,往往存在着这样的"数据集",它们的数值在程序中是稳定的,而且"数据集"中的元素是有限的. 例如星期一到星期日七个数据元素组成了一周的"数据集",春夏秋冬四个数据元素组成了四季的"数据集". 在java中如何更好的使用这些"数据集"呢?因此枚举便派上了用场,以下代码详细介绍了枚举的用法. package com.ljq.test; /** * 枚举用法详解 * * @aut

  • Java 多线程学习详细总结

    目录(?)[-] 一扩展javalangThread类 二实现javalangRunnable接口 三Thread和Runnable的区别 四线程状态转换 五线程调度 六常用函数说明 使用方式 为什么要用join方法 七常见线程名词解释 八线程同步 九线程数据传递 本文主要讲了java中多线程的使用方法.线程同步.线程数据传递.线程状态及相应的一些线程函数用法.概述等. 首先讲一下进程和线程的区别: 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1

  • Java多线程编程详细解释

    目录 一.多线程的优缺点 多线程的优点: 多线程的代价: 二.创建java多线程 1.创建Thread的子类 2.实现Runnable接口 三.线程安全 四.java同步块 五.java线程通信 六.java中的锁 七.java中其他同步方法 八.java中的线程池 参考: 总结 一.多线程的优缺点 多线程的优点: 1)资源利用率更好 2)程序设计在某些情况下更简单 3)程序响应更快 多线程的代价: 1)设计更复杂 虽然有一些多线程应用程序比单线程的应用程序要简单,但其他的一般都更复杂.在多线程

  • Java 高并发二:多线程基础详细介绍

    本系列基于炼数成金课程,为了更好的学习,做了系列的记录. 本文主要介绍 1.什么是线程 2.线程的基本操作 3.守护线程 4.线程优先级 5.基本的线程同步操作 1. 什么是线程 线程是进程内的执行单元 某个进程当中都有若干个线程. 线程是进程内的执行单元. 使用线程的原因是,进程的切换是非常重量级的操作,非常消耗资源.如果使用多进程,那么并发数相对来说不会很高.而线程是更细小的调度单元,更加轻量级,所以线程会较为广泛的用于并发设计. 在Java当中线程的概念和操作系统级别线程的概念是类似的.事

  • java 线程同步详细介绍及实例代码

    java 线程同步 概要: 为了加快代码的运行速度,我们采用了多线程的方法.并行的执行确实让代码变得更加高效,但随之而来的问题是,有很多个线程在程序中同时运行,如果它们同时的去修改一个对象,很可能会造成讹误的情况,这个时候我们需要用一种同步的机制来管理这些线程. (一)竞争条件 记得操作系统中,让我印象很深的有一张图.上面画的是一块块进程,在这些进程里面分了几个线程,所有这些线程齐刷刷统一的指向进程的资源.Java中也是如此,资源会在线程间共享而不是每个线程都有一份独立的资源.在这种共享的情况下

随机推荐