【JavaEE】Thread类及常见的基本用法

文章目录

  • Thread 的常见构造方法
  • Thread 的常见属性
  • Thread的基本用法
    • 创建线程
    • 启动线程
    • 中断线程
    • 线程等待
    • 线程休眠
    • 获取当前线程的引用

Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。每个执行流,也需要有一个对象来描述,类似下图所示,而 Thread 类的对象就是用来描述一个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理。
【JavaEE】Thread类及常见的基本用法_第1张图片

Thread 的常见构造方法

【JavaEE】Thread类及常见的基本用法_第2张图片

Thread 的常见属性

属性 获取方法
ID getId()
名称 getName()
状态 getState()
优先级 getPriority()
是否后台线程 isDaemon()
是否存活 isAlive()
是否被中断 isInterrupted()
  • ID 是线程的唯一标识,不同线程不会重复
  • 名称是各种调试工具用到
  • 状态表示线程当前所处的一个情况,下面我们会进一步说明
  • 优先级高的线程理论上来说更容易被调度到
  • 关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
  • 是否存活,即简单的理解,为 run 方法是否运行结束了
  • 线程的中断问题,下面将进一步说明

Thread的基本用法

创建线程

方法1: 继承 Thread 类
1)继承 Thread 来创建一个线程类

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("这里是线程运行的代码");
   }
}

2)创建 MyThread 类的实例

MyThread t = new MyThread();

3)调用 start 方法启动线程

t.start(); // 线程开始运行

方法2: 实现 Runnable 接口
1)实现 Runnable 接口

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("这里是线程运行的代码");
   }
}

2)创建 Thread 类实例, 调用 Thread 的构造方法时将 Runnable 对象作为 target 参数

Thread t = new Thread(new MyRunnable());

3)调用 start 方法

t.start(); // 线程开始运行

对比上面两种方法:

  • 继承 Thread 类, 直接使用 this 就表示当前线程对象的引用.
  • 实现 Runnable 接口, this 表示的是 MyRunnable 的引用. 需要使用 Thread.currentThread()

其他方法:

  • 匿名内部类创建 Thread 子类对象
       Thread t=new Thread(){
            @Override
            public void run(){
                System.out.println("hello tread");
            }
        };
        t.start();
  • 匿名内部类创建 Runnable 子类对象
        Thread t=new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        });
        t.start();
  • lambda 表达式创建 Runnable 子类对象
       Thread t=new Thread(()->{
            System.out.println("hello");
        });
        t.start();

启动线程

在上面我们已经看到了如何通过覆写 run 方法创建一个线程对象,但线程对象被创建出来并不意味着线程就开始运行了。

  • 覆写 run 方法是提供给线程要做的事情的指令清单
  • 线程对象可以认为是把 李四、王五叫过来了
  • 而调用 start() 方法,就是喊一声:”行动起来!“,线程才真正独立去执行了。
    【JavaEE】Thread类及常见的基本用法_第3张图片

调用 start 方法, 才真的在操作系统的底层创建出一个线程。

中断线程

李四一旦进到工作状态,他就会按照行动指南上的步骤去进行工作,不完成是不会结束的。但有时我们需要增加一些机制,例如老板突然来电话了,说转账的对方是个骗子,需要赶紧停止转账,那张三该如何通知李四停止呢?这就涉及到我们的停止线程的方式了。
目前常见的有以下两种方式:

  1. 可以手动的设置一个标志位(也就是自己创的变量),来控制线程是否要执行结束。
  2. 更好的做法是使用Thread中内置的一个标志位来进行判定,可以通过使用 Thread.interrupted()(静态的方法) 或者Thread.currentThread().isInterrupted()(实例方法) 代替自定义标志位。(推荐此方法)

Thread 内部包含了一个 boolean 类型的变量作为线程是否被中断的标记。

【JavaEE】Thread类及常见的基本用法_第4张图片

示例代码(方法一):

package Thread;
/**
 * 线程的中断
 */
public class Demo {
    private static boolean isQuit=false;
    public static void main(String[] args) {
        Thread t=new Thread(()->{
            while(!isQuit){
                System.out.println("hello thread!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        //只要把这个isQuit设为true,此时这个循环就退出了,进一步的这个run方法就执行完了,再进一步说就是这个线程执行结束了
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        isQuit=true;
        System.out.println("终止t线程执行");
    }
}

结果图如下:
【JavaEE】Thread类及常见的基本用法_第5张图片
示例代码(方法二):

package Thread;

public class Demo1 {
    public static void main(String[] args) {
        Thread t=new Thread(()->{
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //在主线程中,调用interrupt方法,来中断这个线程
        t.interrupt();//让t线程被中断
    }
}

结果图如下:
【JavaEE】Thread类及常见的基本用法_第6张图片
由上面的结果图效果可知,使用t.interrupt() 方法并没有成功让t这个线程中断,具体原因见下图:

【JavaEE】Thread类及常见的基本用法_第7张图片
那么该如何解决这个问题呢,只需在触发异常后退出循环即可,如以下代码所示:

while(!Thread.currentThread().isInterrupted()){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    //当我们触发异常后立即退出循环
                    break;
                }
            }
        });

结果图如下:
【JavaEE】Thread类及常见的基本用法_第8张图片

线程等待

多个线程之间,调度顺序是不确定的。由于线程之间的执行是按照调度器来安排的,而这个过程我们可以视为是“无序,随机的”,但是有些时候,我们需要能够控制线程之间的顺序,而线程等待就是其中一种控制线程执行顺序的手段。此处的线程等待主要是控制线程结束的先后顺序。
基本方法及说明:
【JavaEE】Thread类及常见的基本用法_第9张图片
代码示例如下:

package Thread;

public class Demo2 {
    public static void main(String[] args) {
        Thread t=new Thread(()->{
            for(int i=0;i<5;i++){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        //在主线程中使用等待操作来等待t线程执行结束
        try {
            //t.join();//默认情况下是死等
            t.join(1000);//进入join也会产生阻塞,但阻塞不会一直持续下去,可以执行等待时间,最长等待多久,若超过了时间则此时join直接返回
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在上述代码中,调用这个方法的线程是main线程,是针对t这个线程对象调用的,此时就是让main等待t,调用join后,main线程就会进入阻塞状态(暂时无法在cpu上执行)。

线程休眠

因为线程的调度是不可控的,所以,这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间的。

方法 说明
public static void sleep(long millis) throws InterruptedException 休眠当前线程 millis毫秒
public static void sleep(long millis, int nanos) throwsInterruptedException 可以更高精度的休眠

示例代码如下:

package Thread;

public class Demo4 {
    public static void main(String[] args) throws InterruptedException {
        System.out.println(System.currentTimeMillis());
        Thread.sleep(3*1000);
        System.out.println(System.currentTimeMillis());
    }
}

结果图如下:
在这里插入图片描述

获取当前线程的引用

方法 说明
public static Thread currentThread(); 返回当前线程对象的引用

哪个线程调用的这个currentThread方法,就获取到的是哪个线程的实例。

示例代码如下:

package Thread;

public class Demo3 {
    public static void main(String[] args) {
        Thread t=new Thread(){
            @Override
            public void run() {
               // System.out.println(Thread.currentThread().getName());//得到t这个线程的名字
               System.out.println(this.getName());对于这个代码来说,是通过继承Thread的方式来创建线程,所以此时在run方法中,直接通过this拿到的就是当前Thread的实例
            }
        };
        t.start();
        System.out.println(Thread.currentThread().getName());//得到main线程的名字
    }
}

结果图如下:
在这里插入图片描述
在这里插入图片描述
【JavaEE】Thread类及常见的基本用法_第10张图片

你可能感兴趣的:(JavaEE,java-ee)