Thread线程类 和 Thread的常用方法

目录

一、Thread概述

二、构造方法

三、常用方法

1.1 getId()、getName()、getState()、getPririty()

1.2 start()

1.3 isDaemon()、setDaemon()

1.4 isAlive()

1.5 currentThread()

1.6 Interrupt()、interrupted()、isInterrupted()

1.6.1 方法一:添加共享的标志位

1.6.2  方法二:使用内置的标志位

1.6.3 Java中终止线程不是强制性的

1.7 sleep()

1.8 join()


一、Thread概述

        Thread类是JVM用于管理线程的类,每一个线程都与一个唯一的Thread对象相关联,即每个执行流都由一个Thread对象进行描述,这些对象被JVM组织,用于线程调度和管理。

二、构造方法

构造方法说明
Thread()创建线程对象
Thread(Runnable target)使用Runnable接口实现类对象,创建线程对象
Thread(String name)创建线程对象,并为线程对象命名
Thread(Runnable target, String name)使用Runnable接口实现类对象,创建线程对象,并为线程对象命名
Thread(TreadGroup group, Runnable target)指定线程组,使用Runnable接口实现类对象,创建线程对象

三、常用方法

常用方法说明
getId()获取线程ID
getName()获取线程名
getState()获取线程状态
getPririty()获取线程优先级
start()启动线程
isDaemon()判断线程是否为后台线程(守护线程)
setDaemon()设定线程是否为后台线程(守护线程)
isAlive()判断线程是否“存活”
currentThread()获取当前线程的引用
Interrupt()终止一个线程
interrupted()判断当前线程标志位状态
isInterrupted()判断对象线程标志位状态
sleep()休眠线程
join()阻塞线程

1.1 getId()、getName()、getState()、getPririty()

getId()
获取线程ID,ID是线程的唯一标识,由JVM自动分配并确保唯一性。
getName()
获取线程名,线程名可以自动生成,也可以自定义。线程名可以重复
getState()
获取线程状态,线程的状态有就绪、阻塞等。Java中现成的状态使用枚举保存,可以通过遍历枚举获得所有状态的描述

阅读指针 -> 《Java中线程有多少种状态(State)?状态之间的关系有什么关系?》

<JavaEE> Java中线程有多少种状态(State)?状态之间的关系有什么关系?-CSDN博客文章浏览阅读3次。介绍Java中的线程状态和状态之间的关系有什么关系。https://blog.csdn.net/zzy734437202/article/details/134626843

getPririty()
获取线程优先级,优先级高的线程理论上更容易被调度使用,但是在Java中优先级的效果并不明显。

操作演示:

class MyThread extends Thread{
    @Override
    public void run() {
        for(int i=0;i<5;i++){
            System.out.println("这是MyThread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Thread_Demo6 {
    public static void main(String[] args){
        Thread thread = new MyThread();
        System.out.println("ID:"+thread.getId());
        System.out.println("线程名:"+thread.getName());
        System.out.println("状态:"+thread.getState());
        System.out.println("优先级:"+thread.getPriority());
    }
}

打印结果:

ID:20
线程名:Thread-0
状态:NEW
优先级:5

1.2 start()

start() 启动线程
1)通过重写Thread中的run方法可以创建一个线程对象,再通过调用start()方法,启动这个线程。此时,操作系统中的线程才真正被创建出来。
2)Thread调用start()创建出的线程,底层仍然是调用系统的API来进行创建线程的操作
3)Thread类使用start方法启动线程,对于同一个Thread对象,start方法只能调用一次,需要启动多少个线程,就需要创建多少个Thread对象。
4)start()和run()的区别在于,run方法是提供了线程需要运行的内容,而start方法才是真正让线程运行起来。

同一个Thread对象不能多次调用start方法演示:

class MyThread extends Thread{
    @Override
    public void run() {
        for(int i=0;i<2;i++){
            System.out.println("这是MyThread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("MyThread-run()运行结束");
    }
}
public class Thread_Demo9 {
    public static void main(String[] args) {
        Thread thread = new MyThread();

        //第一次启动线程thread;
        thread.start();

        //第二次启动线程thread;
        thread.start();
    }
}

打印结果:

Exception in thread "main" java.lang.IllegalThreadStateException
    at java.lang.Thread.start(Thread.java:708)
    at Learn_Thread.Demo9.Thread_Demo9.main(Thread_Demo9.java:32)

这是MyThread
这是MyThread
MyThread-run()运行结束


可以看到两次调用start方法,只有一次成功执行,另一次报错IllegalThreadStateException

1.3 isDaemon()、setDaemon()

isDaemon() 判断线程是否为后台线程
setDaemon() 设定线程是否为后台线程
1)daemon的意思是守护,因此也将后台线程称为守护线程。与后台线程相呼应,还有前台线程。
2)代码创建的线程,默认为前台线程。当setDaemon()方法的参数为false时,线程将被设置为前台线程,当参数为true时,线程将被设置为后台线程。
3)前台线程的运行时,将阻止进程结束;后台线程运行时,不会阻止进程结束。
4)为什么要将后台线程称为守护线程?就是说进程需要我就在,进程不要我就走,在背后默默守护进程的线程这个意思?

后台进程的执行演示:

class MyThread extends Thread{
    @Override
    public void run() {
        for(int i=0;i<5;i++){
            System.out.println("这是MyThread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Thread_Demo7 {
    public static void main(String[] args) {
        Thread thread = new MyThread();

        //设置为守护线程(后台线程);
        thread.setDaemon(true);
        //开始thread;
        thread.start();

        //main线程等待两秒后结束,此时守护线程还有代码没打印,但也随之结束了;
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main线程结束");
    }
}

打印结果:

这是MyThread
这是MyThread
main线程结束


启动main线程和thread线程,在main线程sleep两秒结束线程后,可以看到thread线程的run()方法打印结果确实不是预期中的五次“这是MyThread”。这意味着thread线程也随着main线程的结束而结束了。

1.4 isAlive()

isAlive() 判断线程是否“存活”
1)Java中的线程类Thread对象实例,虽然表示一个线程,但这个实例的生命周期与系统内核中的线程的生命周期是不同的。
2)Thread对象创建了就存在,但此时如果调用isAlive()得到的结果将会是false,因为内核中的线程此时还不存在。
3)只有当调用start()启动线程之后,内核中的线程启动,调用isAlive()得到的结果才会是true。
4)当线程运行结束,系统内核中的线程也随之结束,此时虽然Thread对象还存在,但是调用isAlive()得到的结果也将是false。

5)因此,可以简单将这个方法认为是用于判断系统内核中的线程(PCB)是否存在。

从代码层面来讲,可以认为是用于判断run()方法是否执行完毕。

演示判断线程是否“存活”:

class MyThread extends Thread{
    @Override
    public void run() {
        for(int i=0;i<2;i++){
            System.out.println("这是MyThread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("MyThread-run()运行结束");
    }
}
public class Thread_Demo8 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new MyThread();

        //线程已有实例,判断isAlive();
        System.out.println("start前,thread是否存活:"+thread.isAlive());

        //启动线程,判断isAlive();
        thread.start();
        System.out.println("start中,thread是否存活:"+thread.isAlive());

        //等待run()执行结束,此时实例依旧存在,判断isAlive();
        Thread.sleep(5000);
        System.out.println("start结束,thread是否存活:"+thread.isAlive());
    }
}

打印结果:

start前,thread是否存活:false
start中,thread是否存活:true

这是MyThread
这是MyThread
MyThread-run()运行结束
start结束,thread是否存活:false

1.5 currentThread()

currentThread() 获取当前线程的引用
1)在currentThread()方法返回的打印信息中,有三个值,分别代表[线程名,线程优先级,所在线程组]。

获取线程引用操作演示:

public class Thread_Demo11 {
    public static void main(String[] args) {
        //打印main线程信息;
        System.out.println(Thread.currentThread());
    }
}

 打印结果:

Thread[main,5,main]


中括号中的三个值分别代表:线程名,线程优先级,所在线程组。

1.6 Interrupt()、interrupted()、isInterrupted()

如果想要中断(终止)一个线程,可以有多种方法,以下介绍两种:

方法一:通过共享的标志进行线程间沟通。
方法二:调用Interrupt()方法。

1.6.1 方法一:添加共享的标志位

class MyThread extends Thread{
    //设置共享的标志位;
    public volatile boolean isQuit = false;
    @Override
    public void run() {
        //根据标志位的变化,决定后续执行;
        while (!isQuit){
            System.out.println("这是MyThread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("MyThread-run()运行结束");
    }
}
public class Thread_Demo10 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new MyThread();
        //启动线程;
        thread.start();

        //延时两秒后更改标志位;
        Thread.sleep(2000);
        ((MyThread) thread).isQuit = true;
    }
}

打印结果:

这是MyThread
这是MyThread
这是MyThread
MyThread-run()运行结束

1.6.2  方法二:使用内置的标志位

方法说明
public void interrupt()

如果线程处于阻塞状态,则抛出异常;

如果线程不处于阻塞状态,则终止线程。

public static boolean interrupted()判断当前线程标志位状态。
public boolean isInterrupted()判断对象线程标志位状态。
1)上文由程序员手动设置了一个共享的标志位,用于控制线程的执行。Java中也提供了相应的封装好的方法,内置了标志位。使用以上三个方法,一样可以达到控制线程的执行效果。

通过Interrupt()终止线程,然后对线程标志位状态进行判断:

class MyThread extends Thread{
    @Override
    public void run() {
        //使用isInterrupted()判断标志位状态;
        while (!Thread.currentThread().isInterrupted()){
            System.out.println("这是MyThread");

            //打印当前线程标志位的状态;
            System.out.println(Thread.interrupted());
        }
        System.out.println("MyThread-run()运行结束");
    }
}
public class Thread_Demo12 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new MyThread();
        thread.start();

        //延时两秒后更改标志位;
        Thread.sleep(2000);
        thread.interrupt();
    }
}

打印结果: 

......

这是MyThread
false
这是MyThread
false
这是MyThread
false
MyThread-run()运行结束


休眠两秒后,thread通过调用interrupt()方法修改了标志位,线程终止执行。

当更改标志位,但线程处于阻塞状态时:

        在更改标志位时,如果线程因为调用 wait/join/sleep 等方法而阻塞,则此时会抛出异常InterruptedException,并重置终止标志位。此时程序的后续执行通过catch子句中的异常处理方案来决定。
        如果在更改标志位时,线程非为阻塞状态,则标志位不会重置,可以通过interrupted()或isInterrupted()进行判断。
class MyThread extends Thread{
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()){
            System.out.println("这是MyThread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("MyThread-run()运行结束");
    }
}
public class Thread_Demo14 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new MyThread();
        thread.start();

        //等待两秒后唤醒;
        Thread.sleep(2000);
        thread.interrupt();
    }
}

打印结果:

这是MyThread
这是MyThread
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at Learn_Thread.Demo14.MyThread.run(Thread_Demo14.java:16)

这是MyThread
这是MyThread
这是MyThread


可以看到,在抛出异常后,仍然继续打印,这意味着原先由interrupt()方法修改的标志位,在sleep唤醒时,又被重置为false了。

在异常处理中,加入更多的处理方法:

class MyThread extends Thread{
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()){
            System.out.println("这是MyThread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                //在异常处理中加入break,跳出循环;
                break;
            }
        }
        System.out.println("MyThread-run()运行结束");
    }
}
public class Thread_Demo14 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new MyThread();
        thread.start();

        //等待两秒后唤醒;
        Thread.sleep(2000);
        thread.interrupt();
    }
}

打印结果:

这是MyThread
这是MyThread
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at Learn_Thread.Demo14.MyThread.run(Thread_Demo14.java:16)

MyThread-run()运行结束


线程通过interrupt()方法修改了标志位,但由于线程此时大概率处于sleep(即,阻塞状态),因此,抛出异常,并将标志位重置。

这里线程会终止,是由异常处理中的break跳出循环得到的结果。

1.6.3 Java中终止线程不是强制性的

操作系统中的API:提供了强制终止线程的操作,无论线程执行到何种程度,都强行结束线程。
Java中的的API:终止线程需要对应线程互相配合,而不是直接“剪断”。
优劣:强制结束线程的方式更“随心所欲,为所欲为”,但如果线程执行过程中被强行终止,可能导致出现一些临时性质的“错误”数据。而相互配合的线程终止,虽然使终止线程时需要考虑的事情变多了,但也使得线程的终止更“安全”,系统运行更稳定了

1.7 sleep()

方法说明
public static void sleep(long millis) throws InterruptedException

以毫秒级别的精度,指定休眠时间

public static void sleep(long millis, int nanos) throws InterruptedException以纳秒级别的精度,指定休眠时间
1)sleep()方法只能保证实际休眠时间大于等于参数设置的休眠时间。
2)sleep被提前唤醒(如被上文的interrup唤醒)时,会抛出异常,并将Thread对象的标志位重置为false。

sleep被提前唤醒时,为什么要重置标志位?

sleep重置标志位,可以给程序员更多的“可操作空间”。

通过抛出异常,处理异常,程序的后续执行可以且不仅可以让线程立即结束,增加了代码的灵活性。

1.8 join()

join() 等待线程结束
join(long millis) 等待线程结束,指定最长等待时间
join(long millis, int nanos) 等待线程结束,以纳秒级别的精度指定最长等待时间
1)由于随即调度,抢占式执行,多线程的执行顺序是不确定的。但是通过应用程序中的API,可以影响到线程的结束顺序
class MyThread extends Thread{
    @Override
    public void run() {
        for(int i=0;i<5;i++){
            System.out.println("这是MyThread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("MyThread-run()运行结束");
    }
}
public class Thread_Demo13 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new MyThread();
        thread.start();

        //让主线程阻塞,保证MyThread先运行完;
        thread.join();
        System.out.println("在MyThread结束后打印");
    }
}

打印结果:

这是MyThread
这是MyThread
这是MyThread
这是MyThread
这是MyThread
MyThread-run()运行结束
在MyThread结束后打印


可以看到,main线程中的“在MyThread结束后打印”确实是在thread线程结束后才打印的。


阅读指针 -> 《 多线程重点!! -- 线程安全 》

什么是线程安全?产生线程不安全的原因和处理方式介绍了线程安全问题的概念。介绍了出现线程不安全的原因,以及其处理方式。这是多线程编程的重点。https://blog.csdn.net/zzy734437202/article/details/134702692

  • 22
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值