【多线程】认识Thread类及其常用方法

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

📄前言
本文是对以往多线程学习中 Thread类 的介绍,以及对其中的部分细节问题进行总结。


之前的文章介绍过线程的引入能够更好地处理程序的并发执行问题。在Java中,线程的创建方式之一是通过 Thead类 (Thead封装了操作系统提供的API,使我们创建的线程能够系统的调度)。接下来我们先了解一下线程的创建方法。

一. 线程的 创建和启动

通过 Thread类 创建线程的方式总体来说可以分为以下两种:

  1. 继承 Thread类,重写类中的 run() 方法
  2. 实例化Thread类,实现 Runnable 接口,重写接口中的 run() 方法(通过Thread的构造方法传参 间接重写run() 方法)

这里我们应该可以发现一个共同点,这两种方法都需要重写 run() 方法,毫无疑问 run() 方法就是线程创建后执行代码逻辑的 “入口”方法,通过查看源码我们发现 Thread类 继承了Runnable,而 run() 方法就是该接口中的抽象方法。(如下)
【多线程】认识Thread类及其常用方法,java,笔记
【多线程】认识Thread类及其常用方法,java,笔记

🍆1. 通过继承 Thread 类创建线程

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("hello thread!");
        }
        System.out.println("线程结束!");
    }
}

🍅2. 通过实现 Runnable 接口创建线程

【多线程】认识Thread类及其常用方法,java,笔记

class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("hello thread !");
        }
        System.out.println("线程结束 !");
    }
}

public class Demo2 {

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(new MyRunnable());
    }

}

🥦3. 其他方法创建线程(本质上为上面两种写法的变形)

🥑3.1 使用 Thread 的匿名内部类

Thread t = new Thread(){
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("hello thread !");
        }
        
        System.out.println("线程结束 !");
    }
};

🥬3.2 使用匿名内部类实现 Runnable 接口

Thread t = new Thread(new Runnable() {
   @Override
    public void run() {
		while(true) {
		    System.out.println("hello thread !");
		}
		
		System.out.println("线程结束 !");
   	}
});

🥒3.3 使用 lambda 表达式实现 Runnable 接口 (推荐,更加简洁!!!)

Thread t = new Thread(() -> {
	while(true) {
	    System.out.println("hello thread !");
	}
	System.out.println("线程结束 !");
});

🍉4. 线程的启动(关于 start方法 和 run方法 的思考)

其实线程的启动方式很简单,直接调用 Thread类中的 start()方法 即可启动线程,为了观察新创建的线程和 “主线程main” 的并发执行效果,代码中调用 Thread类的类方法 sleep(), 让每次语句执行后休眠 1s。(代码及程序运行效果如下)

public static void main(String[] args) throws InterruptedException {
	Thread t = new Thread(() -> {
	   for(int i = 0; i < 5; i++) {
	       System.out.println("hello thread !");
	       try {
	           Thread.sleep(1000);
	       } catch (InterruptedException e) {
	           throw new RuntimeException(e);
	       }
	   }
	});
	System.out.println("线程启动 !");
	t.start();
	
	// 主线程
	for(int i = 0; i < 5; i++) {
	    System.out.println("hello main !");
	    Thread.sleep(1000);
	}
}

【多线程】认识Thread类及其常用方法,java,笔记

通过上面代码的执行结果我们容易知道:创建的线程执行的代码其实就是 run() 方法中的代码,那么我们是否可以通过执行 t.run() 来代替 t.start() 呢?
答案很明显是否定的,因为直接调用 run() 方法本质上与一个自定义函数的调用并无任何差异。我们都知道,在程序的某处调用一个函数,程序会在该函数执行结束后才继续执行后面的代码,因此不能使用 t.run() 来代替 t.start()。
总结:调用run() 方法只是一次简单的函数调用,程序依旧是顺序执行;只有调用 start() 才能在操作系统真正创建一个主线程并发执行的线程


二. Thread类的属性和常用方法

🍚1. Thread类的主要属性

【多线程】认识Thread类及其常用方法,java,笔记
注意:所有创建的线程默认为前台线程,只有当所有前台线程运行结束后,程序才会结束,主线程main运行结束,不会影响其他前台线程的运行。(可以通过setDaemon()方法将线程设置为后台线程)

🍥2. 线程休眠

【多线程】认识Thread类及其常用方法,java,笔记
代码示例及程序运行结果如下:让新线程每1s进行一次打印,主线程每2s进行一次打印(注意:使用sleep()方法需要处理可能抛出的异常)

public static void main(String[] args) {
        
	Thread t = new Thread(() -> {
	    for (int i = 0; i < 6; i++) {
	        System.out.println("hello thread !");
	        try {
	            Thread.sleep(1000);
	        } catch (InterruptedException e) {
	            e.printStackTrace();
	        }
	    }
	});
	t.start();
	
	for (int i = 0; i < 3; i++) {
	    System.out.println("hello main !");
	    try {
	        Thread.sleep(2000);
	    } catch (InterruptedException e) {
	        e.printStackTrace();
	    }
	}
}

【多线程】认识Thread类及其常用方法,java,笔记

🍭3. 线程等待

【多线程】认识Thread类及其常用方法,java,笔记
注意事项:若有两个线程分别为 t1 和 t2,在 t1 线程中调用 t2.join(),则表示 t1线程应等待 t2线程运行结束后再继续运行,即谁调用谁等待

代码实例和程序运行结果如下:让主线程进行一次打印后,等待新线程结束再继续运行。

public static void main(String[] args) {
    Thread t = new Thread(() -> {
        for (int i = 0; i < 5; i++) {
            System.out.println("hello thread !");
        }
    });
    t.start();

    for (int i = 0; i < 5; i++) {
        System.out.println("hello main !");
        if(i == 0) {
            try {
                t.join();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

【多线程】认识Thread类及其常用方法,java,笔记

🍦 4. 获取线程实例

在线程的创建过程中,由于 Thread类 还没有被实例化,因此不能通过类对象的引用变量直接得到该对象,而应该使用 Thread类提供的方法得到该对象的引用。
【多线程】认识Thread类及其常用方法,java,笔记

🧊5. 线程的中断 (关于线程中断的细节!!!)

在正式了解线程中断的方法之前,我们需要知道一个基本的事实:
Java中线程的中断方式并不是让一个正在运行的线程直接中止,而是以一种 “通知的方式” 告诉线程,“你当前应该结束线程了”,而具体是否立即结束当前该线程,由线程接收通知后的代码处理逻辑决定

==========

关于线程的中断方法,Thread类 提供了以下三个方法:
【多线程】认识Thread类及其常用方法,java,笔记

Thread类收到通知的方式有以下两种情况:

  1. 当前线程因 sleep/join/wait 等方法引起阻塞而挂起,以 InterruptedException 异常的形式通知,并清除中断标志,此时线程的阻塞状态立即结束,而是否立即结束由 try/catch 中catch的处理逻辑决定。(注意:线程是否有后续的处理权,取决于该线程是否有能引起阻塞状态的代码
  2. 当前线程处于正常运行状态,将中断标志位设置为true。

==========

  1. 当线程处于阻塞状态时,存在以下三种对应的处理方式:
    1)不管这个通知,继续运行。 2)直接结束线程。 3)进行一些特定的收尾工作再结束线程
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) {
                // 1. 无视通知
                e.printStackTrace();

                // 2. 直接退出
                // break;

                // 3. 进行收尾工作,再退出
                // System.out.println("此处是后续的处理代码");
                // break;
            }
        }
    });
    t.start();

    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    t.interrupt();
}

【多线程】认识Thread类及其常用方法,java,笔记
【多线程】认识Thread类及其常用方法,java,笔记
【多线程】认识Thread类及其常用方法,java,笔记

看到第一个程序运行的结果,不知道大家会不会有一个疑惑,使用 isInterrupted()方法是不会清除标志位的,那 3s过后该线程不应该只打印3次 “hello thread !”,接着由于循环的判断条件为 false 而直接退出吗?
其实 isInterrupted() 方法调用后确实不会清除标志位,程序继续运行的原因是因为3s后主线程调用interrupt()方法,解除了 t线程 的阻塞状态,同时 sleep() 引起的异常会顺便清除标志位。

因此,如果将代码改为下面的写法,线程会在当前代码逻辑执行完毕 或 进入下一次循环判断后直接退出。

public static void main(String[] args) {
    Thread t = new Thread(() -> {
        while (!Thread.currentThread().isInterrupted()) {
            System.out.println("hello thread !");
        }
        System.out.println("---3s后 t线程退出---");
    });
    t.start();

    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    t.interrupt();
}

程序运行结果如下:
【多线程】认识Thread类及其常用方法,java,笔记

======

  1. 当前线程处于正常运行状态,将中断标志位置为true。
    从前面我们可以知道:Thread.interrupted() 判断后会清除标志位,isInterrupted() 不会清除标志位,现有以下代码:
private static int count = 100;

public static void main(String[] args) {
    Thread t = new Thread(() -> {
       for (int i = 1; count > 0 || Thread.currentThread().isInterrupted(); i++) {
           if(Thread.currentThread().isInterrupted()) {
               System.out.println("我当前收到中断通知了,这是第 " + i + " 次打印");
           } else {
               System.out.println("我当前没有收到中断通知, 这是第 " + i + " 次打印");
           }
           count--;
           if(i == 150) break;
       }
    });
    t.start();


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

    t.interrupt();
}

程序的运行结果如下:
【多线程】认识Thread类及其常用方法,java,笔记
这段代码的运行结果是:程序在第58次打印时标志位发生了改变,并且后续的打印不再发生改变,可以预见的是:如果没有使用 i 的值判断使循环退出,程序将会无终止地进行打印。原因就是 isInterrupted()方法 不会清除标志位,因此判断条件永远为真

若把循环中的方法修改为 Thread.interrupted(),程序的运行结果就发生了改变:

private static int count = 50;

public static void main(String[] args) {
    Thread t = new Thread(() -> {
       for (int i = 0; count > 0 || Thread.interrupted(); i++) {
           if(Thread.interrupted()) {
               System.out.println("我当前收到中断通知了,这是第 " + i + " 次打印");
           } else {
               System.out.println("我当前没有收到中断通知, 这是第 " + i + " 次打印");
           }
           count--;
       }
    });
    t.start();


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

    t.interrupt();
}

【多线程】认识Thread类及其常用方法,java,笔记
可以看到在第27次打印时标志位发生了改变,说明此时线程收到了中断通知,但后面的打印内容为“没有收到通知”,且程序最终打印了50次便结束了,这都说明了调用 Thread,interrupted()方法 后标志位被清除了


以上就是本篇文章的全部内容了,如果这篇文章对你有些许帮助,你的点赞、收藏和评论就是对我最大的支持。
另外,文章可能存在许多不足之处,也希望你可以给我一点小小的建议,我会努力检查并改进。文章来源地址https://www.toymoban.com/news/detail-802634.html

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

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

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

相关文章

  • 【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)
  • String类的学习笔记(上):介绍String类及其常用方法的使用

    本文介绍了Java中用来描述操作字符串的String类,和其一些常用的基本操作方法,字符串的创建输出,字符串对象的比较,字符串查找,字符串的转化,字符串的替换,字符串拆分,字符串截取,和大小写转换,去除左右空格,子字符串包含,学会使用这些方法,能更方便的使用操作字符串~ 前言

    2023年04月23日
    浏览(41)
  • 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)
  • 常用的Java线程阻塞业务方法

    在Java中常常使用到多线程处理一些业务,但是也会遇到多线程处理时带来的业务逻辑时序不对问题,例如需要等待一个业务处理完或者下一步的逻辑需要等待多线程得到的结果,下面是几种常用的方法 使用 Thread.join() 方法 对于已知的线程列表,可以遍历列表并调用每个线程

    2024年04月29日
    浏览(32)
  • Linux——认识Linux的目录结构 & 常用命令 & vim命令 & 权限及其控制

    一切皆文件 文件分类 【安装】Linux环境下的 JDK的安装 安装配置 环境变量 1.进程kill -9 运行窗口退出 2.ctrl c退出 ls -a 查看所有文件(包含隐藏) ​ ls -la 查看所有文件详细信息 查看当前文件夹下的文件 在 Linux 系统中,ls 和 ll 命令都是用来列出目录内容的命令,它们的区别

    2024年02月16日
    浏览(52)
  • 【多线程初阶】Thread类常见方法以及线程的状态

    本文是属于多线程初阶内容系列的, 如果还没有学习过之前文章的, 请先移步博主的之前的文章进行学习, 本文就是在学会线程的创建后, 再带大家认识一下 Thread 类以及其常见的方法, 再给大家讲解一下线程都有哪些状态. 关注收藏, 开始学习吧🧐 通过我们上篇文章的学习, 我

    2024年02月16日
    浏览(31)
  • Docker——认识Docker & 常用命令 & Linux中安装docker & 常见问题及其解决

    1.认识docker,docker和虚拟机对比; 2.docker的架构,客户端,镜像,容器,仓库; 3.docker常用的命令,docker exec,inspect,ps; 4.Linux安装docker,更换镜像源,ipv4转发开启; Docker是一个开源的应用容器引擎,开发者可以打包自己的应用到容器里面,然后迁移到其他机器的docker应用

    2024年02月16日
    浏览(29)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包