【JavaEE】_多线程Thread类及其常用方法

这篇具有很好参考价值的文章主要介绍了【JavaEE】_多线程Thread类及其常用方法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1. Thread类常用构造方法

2. Thread类的几个常见属性

3. 启动一个线程

4. 中断一个线程

4.1 方法1:手动设置标志位

4.2 方法2:使用Thread内置的标志位

5. 等待一个线程

6. 获取当前线程引用

7. 休眠当前线程


1. Thread类常用构造方法

方法

说明

Thread() 创建线程对象
Thread(Runnable target) 使用Runnable对象创建线程对象
Thread(String name) 创建线程对象并命名
Thread(Runnable target,String name) 使用Runnable对象创建线程对象并命名
Thread(ThreadGroup group,Runnable target) 使用线程组对线程分组管理

前两个构造方法前文已经使用,此处不再赘述,此处主要展示带有命名的线程对象的创建:

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

        Thread t1 = new Thread(()->{
            while(true){
                System.out.println("Hello Thread1.");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "Thread t1");
        t1.start();
        Thread t2 = new Thread(()->{
            while(true){
                System.out.println("Hello Thread2.");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "Thread t2");
        t2.start();
    }
}

运行代码并根据jdk安装路径打开bin文件找到jconcole.exe文件:

【JavaEE】_多线程Thread类及其常用方法,JavaEE,java-ee,java,intellij-idea

打开在本地进程中连接对应进程:
【JavaEE】_多线程Thread类及其常用方法,JavaEE,java-ee,java,intellij-idea

进入界面选择进程后,就可以看到正在运行的线程,点击线程还可以显示线程的执行位置:

【JavaEE】_多线程Thread类及其常用方法,JavaEE,java-ee,java,intellij-idea

注:(1)给Thread命名仅便于调试时对线程进行区分,对于线程执行没有其他影响;

(2)java进程启动后不只有我们手动编写的线程,还有一些JVM自己创建的线程用于其他的不同工作,比如收集统计调试信息,监听网络链接等等;

2. Thread类的几个常见属性

属性 获取方法
ID getId()
名称 getName()
状态 getState()
优先级 getPriority()
是否后台程序 isDaemon()
是否存活 isAlive()
是否被中断 isInterrupted()

注:(1)ID是线程的唯一标识,不同线程不会重复;

(2)获得的名称也就是构造时指定的名称,主要用于调试时便于查看线程状态;

(3)状态表示当前线程所处的状态,后续详细介绍;

(4)优先级高的线程理论上来说更容易被调度到;

(5)后台线程:

①  如果一个线程是后台线程,就不影响进程退出,main方法执行完毕后就会结束进程,并强行终止后台线程;

如果一个线程是前台线程,即使main方法执行完毕,也必须等前台线程执行完毕1才能结束进程,JVM会在一个进程的所有非后台线程结束后才会结束运行

②  代码里手动创建的线程(包括main线程)默认是前台线程,其他的jvm自带的线程都是后台线程,也可以使用setDaemon将一个前台线程设置为后台线程;

③ 注意区别线程调度与后台线程;

(6)是否存活可以理解为:操作系统中对应的线程是否正在运行;

Thread t 对象的生命周期和内核中对应的线程生命周期并不完全一致,t对象被创建后,在调用start方法之前,系统中是没有对应线程的,在run方法执行完毕后,系统中的线程就销毁了,但是t对象还可能存在;

【JavaEE】_多线程Thread类及其常用方法,JavaEE,java-ee,java,intellij-idea

在调用start之后,run执行完之前,isAlive就是返回true,

如果是调用start之前,run执行完之后,isAlive就是返回false;

(7)线程的中断问题,后续详细介绍;

3. 启动一个线程

public class Demo2 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            while(true){
                System.out.println("Hello Thread.");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t.start();

        while(true){
            System.out.println("Hello Main.");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

运行以上代码,截取部分输出结果如下:

【JavaEE】_多线程Thread类及其常用方法,JavaEE,java-ee,java,intellij-idea

将上文代码的t.start()语句更换为t.run()语句再试运行,截取部分输出结果如下:

【JavaEE】_多线程Thread类及其常用方法,JavaEE,java-ee,java,intellij-idea

因为run方法只是一个普通的方法,在main线程中调用run,其实并没有创建心得线程,上文中的循环语句依然是在main线程中执行的,在一个线程中,代码就会按照从前至后的顺序进行运行,此时就只会在第一个循环结构中一直打印Hello Thread.;

可以通过修改循环条件进行验证:

public class Demo2 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            for(int i=0;i<3;i++){
                System.out.println("Hello Thread.");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t.run();

        for(int i=0;i<2;i++){
            System.out.println("Hello Main.");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

再运行代码,输出结果为:

【JavaEE】_多线程Thread类及其常用方法,JavaEE,java-ee,java,intellij-idea  

注:run方法只是一个遵循串型执行的、描述任务内容的普通调用方法;

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

4. 中断一个线程

中断一个线程的关键在于使当前线程对应的run方法执行完

但对于特殊的main线程来说,需要main方法执行完线程才完;

4.1 方法1:手动设置标志位

此处的标志位是自己创建的变量,来控制线程是否要执行结束:

// 通过设置标志位中断一个线程
public class Demo2 {
    private static boolean flag = true;
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while(flag){
                System.out.println("Hello Thread.");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        //在主线程中通过更改flag变量的取值来操作t线程是否结束
        //当flag为假时不再进入循环
        Thread.sleep(3000);
        flag = false;
    }
}

输出结果为:

【JavaEE】_多线程Thread类及其常用方法,JavaEE,java-ee,java,intellij-idea

注:(1)t线程开始执行后,当3s时间截止后,标志位flag被修改,此时t线程run方法所在循环被终止,t线程终止,再后的main方法也执行完毕,main线程也终止,同时进程也终止了;

(2)此处因为多个线程共用一个虚拟地址空间,因此main修改的flag与t线程判定的flag是同一个值;

(3)以上写法并不严谨,后续再进行介绍,同时由于手动设置标志位来中断一个线程不能及时响应,尤其是在sleep休眠时间比较久时,故而引出中断一个线程的第二种方法;

4.2 方法2:使用Thread内置的标志位

 判断线程是否被中断:Thread.currentThread().isInterrupted()实例方法,其中currentThread可以获取到当前线程的对象的引用;

②  设置线程中断位置:t.interrupt()

(更推荐② 一个代码中的线程可能有很多,随时哪个线程都有可能终止,①方法表示一个程序只有一个标志位,但是②方法判定的标志位是Thread的普通成员,每个实例都有自己的标志位;)

代码示例1:

public class Demo4 {
    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();
        //在主线程中调用interrupt方法来中断t线程
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //中断t线程
        t.interrupt();
    }
}

运行结果如下:

【JavaEE】_多线程Thread类及其常用方法,JavaEE,java-ee,java,intellij-idea

 注:(1)调用interrupt()方法可能产生两种情况:

如果调用线程处于就绪状态,则是设置线程标志位为true;

如果调用线程处于阻塞状态(sleep),interrupt方法就会强行打破休眠,则sleep会触发InterruptException异常,导致线程从阻塞状态被唤醒,将线程从sleep中被唤醒时,又会将原先设定的标志位再设置回false,即清空了标志位;

不只是sleep方法可以将线程意外中断后清除标志位,像join、wait等可以造成线程堵塞的方法都有类似于清除标志位的设定;

(2)上文代码一旦出发了异常就会进入catch语句,在catch语句中,就打印出当前异常位置的代码调用栈,打印完毕后继续运行;

这显然不是我们期待的结果,故而需要修改:

代码示例2:

public class Demo3 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("Hello Thread.");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    //触发异常后稍后几秒再终止t线程
                    try{
                        Thread.sleep(1000);
                    }catch(InterruptedException ex){
                        ex.printStackTrace();
                    }
                    //触发异常后立刻退出循环
                    break;
                }
            }
        });
        t.start();
        Thread.sleep(3000);
        t.interrupt();     //通知终止t线程
    }
}

在t线程抛出Interrupted异常后立刻停止工作,输出结果为:

【JavaEE】_多线程Thread类及其常用方法,JavaEE,java-ee,java,intellij-idea

也可看出interrupt唤醒sleep中的线程后,要将标志位重新置回的意义:

t.interrupt()只是main线程通知t线程终止,而t线程是否终止取决于其本身,重新将标志位置回意味着可以在t线程内部选择继续执行、终止、稍后终止,否则t线程只能立刻终止。

5. 等待一个线程

多个线程之间的调度顺序是不确定的,线程之间的执行顺序是调度器无序、随机执行的,有时我们需要控制线程之间的执行顺序,线程等待就是控制线程结束先后顺序的重要方法,哪个线程调用join()方法,哪个线程就会阻塞等待对应线程的run方法执行结束为止。

代码示例1:当被等待的线程尚未执行完毕时:

public class Demo4 {
    public static void main(String[] args) {
        Thread t =  new Thread(()->{
           for(int i=0;i<3;i++){
               System.out.println("Hello Thread.");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        });
        t.start();
        System.out.println("Hello Main");
        //让main线程等待t线程的run方法执行完毕
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Hello Main");
    }
}

输出结果为:

【JavaEE】_多线程Thread类及其常用方法,JavaEE,java-ee,java,intellij-idea

注:(1)上文代码是main线程调用join()方法,是针对t这个线程对象调用的,此时就是让main等待t,调用join()方法后,main线程就会进入阻塞状态,直到t的run()方法执行完毕后,main线程才会继续执行; 

(2)干预两个线程的执行顺序体现在:通过线程等待控制先让t结束,main后结束;

(3)注意区别干预线程执行顺序与优先级的区别:优先级是操作系统内部内核进行线程调度使用的参考量,在用户层面代码不能完全干预或控制,线程执行顺序是代码中控制的先后顺序;

代码示例2:当被等待的线程已经执行完毕时:

public class Demo4 {
    public static void main(String[] args) {
        Thread t =  new Thread(()->{
           for(int i=0;i<3;i++){
               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();
        }
        System.out.println("Hello Main");
        //让main线程等待t线程的run方法执行完毕
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Hello Main");
    }
}

输出结果为:

【JavaEE】_多线程Thread类及其常用方法,JavaEE,java-ee,java,intellij-idea

如果被等待的线程在其他线程尚未执行到等待该线程的语句时该线程已经执行完毕,则join直接返回;

注:join()操作默认情况下是持续等待的,为了避免这种机制带来的麻烦,join提供了另外一个版本,可以设定最长等待时间(等待时间上限),具体内容请看示例3:

代码示例3:指定join的等待上限:

public class Demo4 {
    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();
        System.out.println("Hello Main");
        //让main线程等待t线程的run方法执行完毕
        try {
            t.join(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Hello Main");
    }
}

输出结果为: 

【JavaEE】_多线程Thread类及其常用方法,JavaEE,java-ee,java,intellij-idea

上文代码t.join(2000)代表:main线程进入阻塞状态等待t线程run方法执行完毕,如果2s之内t线程执行结束了,此时join直接返回,如果3s之后t线程还未结束,join也直接返回;

6. 获取当前线程引用

Thread.currentThread()是一个静态方法,能获取当前线程实例的引用,哪个线程调用则获取哪个线程实例的引用;

代码示例1:通过继承Thread类创建线程:

public class Demo6 {
    public static void main(String[] args) {
        Thread t = new Thread("Thread t"){
            @Override
            public void run(){
                System.out.println(Thread.currentThread().getName());
            }
        };
        t.start();
        //main线程
        System.out.println(Thread.currentThread().getName());
    }
}

输出结果为:

【JavaEE】_多线程Thread类及其常用方法,JavaEE,java-ee,java,intellij-idea

由于本方式是通过继承Thread类来创建接口的,故而也可以通过this.getName()方式获取当前类的对象的引用:

public class Demo6 {
    public static void main(String[] args) {
        Thread t = new Thread("Thread t"){
            @Override
            public void run(){
                //System.out.println(Thread.currentThread().getName());
                System.out.println(this.getName());
            }
        };
        t.start();
        //main线程
        System.out.println(Thread.currentThread().getName());
    }
}

代码示例2:通过实现Runnable接口创建接口:

这种方式创建线程,则不能通过this.getName()方法获取当前对象的引用:

Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(this.getName());
            }
        });

由于此时this不是指向Thread类型了,而是指向Runnable,Runnable只是一个任务,没有name属性;

只能通过Thread.currentThread().getName()方式获取当前对象的引用:
 

        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                Thread.currentThread().setName("Thread t");
                System.out.println(Thread.currentThread().getName());
            }
        });
        t.start();

输出结果为:

【JavaEE】_多线程Thread类及其常用方法,JavaEE,java-ee,java,intellij-idea

7. 休眠当前线程

在前文已经介绍过:对于一个线程的进程,在系统中是通过PCB描述的,通过双向链表组织的;

对于一个有多个线程的进程,每个线程都有一个PCB,一个进程对应的就是一组PCB,PCB上有一个tgroupId,这个id就相当于是进程的id,同一个进程中的若干个tgroupId是相同的;

PCB即process control block进程控制块,其实在Linux内核是不区分进程与线程的,只有在程序员写应用程序代码时才会进行区分,Linux内核只识别辨认PCB,在内核中线程被称为轻量级进程;

实际在操作系统中调度线程的时候,会从就绪队列中挑选合适的CPB到CPU上运行,而执行了sleep()或join()语句的PCB位于阻塞队列就无法上CPU,只有当休眠结束,再次进入就绪队列才会在后续上CPU被执行;

让线程休眠本质就是让该线程不参与调度了,如令该线程休眠1000ms,其实在该线程从阻塞队列被迁移回就绪队列后并不会被立刻调度,往往令休眠的线程的休眠时间要超过手动设置的时间;文章来源地址https://www.toymoban.com/news/detail-828031.html

到了这里,关于【JavaEE】_多线程Thread类及其常用方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JAVA深化篇_29—— 线程使用之线程联合以及Thread类中的其他常用方法【附有详细说明及代码案例】

    线程联合 当前线程邀请调用方法的线程优先执行,在调用方法的线程执行结束之前,当前线程不能再次执行。线程A在运行期间,可以调用线程B的join()方法,让线程B和线程A联合。这样,线程A就必须等待线程B执行完毕后,才能继续执行。 join方法的使用 join()方法就是指调用该

    2024年02月05日
    浏览(36)
  • Java多线程 -Thread类的常用API

    Thread常用API说明 : Thread常用方法:获取线程名称getName()、设置名称setName()、获取当前线程对象currentThread()。 至于Thread类提供的诸如:yield、join、interrupt、不推荐的方法 stop 、守护线程、线程优先级等线程的控制方法,在开发中很少使用,这些方法会在高级篇以及后续需要用到

    2024年02月21日
    浏览(39)
  • JavaEE初阶:多线程 - Thread 类的基本用法

    上次我们了解了多线程的五种创建方法,今天来学习Thread的基本用法。 目录 run和start Thread常见的构造方法 Thread的几个常见属性 后台线程 是否存活 线程终止 1.使用标志位 2.使用Thread自带的标志 等待线程 首先需要理解Thread的run和start的区别: run描述了线程要做的工作,star

    2024年02月12日
    浏览(24)
  • 【Java系列】详解多线程(二)——Thread类及常见方法(上篇)

    个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【Java系列专栏】【JaveEE学习专栏】 本专栏旨在分享学习Java的一点学习心得,欢迎大家在评论区交流讨论💌 我们先来回顾一下线程与进程之间的联系。 我们知道多进程

    2024年02月04日
    浏览(38)
  • 【Java系列】详解多线程(二)——Thread类及常见方法(下篇)

    个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【Java系列专栏】【JaveEE学习专栏】 本专栏旨在分享学习Java的一点学习心得,欢迎大家在评论区交流讨论💌 在操作系统中创建线程时,通常会同时创建相应的PCB并将其

    2024年02月04日
    浏览(31)
  • 【Java中的Thread线程的简单方法介绍和使用详细分析】

    提示:若对Thread没有基本的了解,可以先阅读以下文章,同时部分的方法已经在如下两篇文章中介绍过了,本文不再重复介绍!! 【Java中Tread和Runnable创建新的线程的使用方法】 【Java中的Thread线程的七种属性的使用和分析】 提示:以下是本篇文章正文内容,下面案例可供参

    2024年02月15日
    浏览(30)
  • 【JavaEE基础学习打卡03】Java EE 平台有哪些内容?

    📜 本系列教程适用于Java Web初学者、爱好者,小白白。我们的天赋并不高,可贵在努力,坚持不放弃。坚信量最终引发质变,厚积薄发。 🚀 文中白话居多,尽量以小白视角呈现,帮助大家快速入门。 🎅 我是 蜗牛老师 ,之前网名是 Ongoing蜗牛 ,人如其名,干啥都慢,所以

    2024年02月12日
    浏览(34)
  • 【JavaEE基础学习打卡02】是时候了解Java EE了!

    📜 本系列教程适用于 Java Web 初学者、爱好者,小白白。我们的天赋并不高,可贵在努力,坚持不放弃。坚信量最终引发质变,厚积薄发。 🚀 文中白话居多,尽量以小白视角呈现,帮助大家快速入门。 🎅 我是 蜗牛老师 ,之前网名是 Ongoing蜗牛 ,人如其名,干啥都慢,所

    2024年02月12日
    浏览(33)
  • java复习-线程常用操作方法

    线程的主要方法都定义在 Thread类 之中,因此此处就是学习Thread中的方法。 构造方法: public Thread(Runnable target, String name) 设置名字: public final synchronized void setName(String name) 取得名字: public final String getName() 范例1:观察线程的名字 结果: 线程A 线程B Thread-0 总结: 说明开发

    2024年02月08日
    浏览(28)
  • Java 进阶(8) 线程常用方法

    方法名 说明 public static void sleep(long millis) 当前线程主动休眠 millis 毫秒。 public static void yield() 当前线程主动放弃时间⽚,回到就绪状态,竞争下⼀次时间⽚。 public final void join() 允许其他线程加⼊到当前线程中。 public void setPriority(int) 线程优先级为1-10,默认为5,优先级越⾼,

    2023年04月16日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包