JavaEE多线程-创建线程(Thread)

news2024/11/18 7:38:18

目录

  • 一、线程(Thread)
    • 1.1 Thread类中的构造方法
    • 1.2 启用线程的方法
  • 二、创建第一个多线程
  • 三、多线程并发执行简单演示
  • 四、多线程并发执行的优势
  • 五、Thread的常见构造方法和属性
    • 5.1 属性
    • 5.2 方法
  • 六、中断线程
  • 七、线程等待

一、线程(Thread)

线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对用户层提供了一些 API 供用户使
用(例如 Linux 的 pthread 库).
Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进行了进一步的抽象和封装.

1.1 Thread类中的构造方法

在这里插入图片描述

1.2 启用线程的方法

在这里插入图片描述

二、创建第一个多线程

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

class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("hello Thread");
    }
}

2.创建MyThread 类的实例

MyThread t = new MyThread();

3.调用start方法启动线程

t.start();

方法二:实现Runnable接口
1.实现Runnable接口

class MyRunnable implements Runnable {
	@Override
    public void run() {
        System.out.println("hello Thread");
    }
}

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

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

3.调用 start 方法

t.start();

方法三:匿名内部类创建Thread子类对象

	Thread t = new Thread() {
		@Override
		public void run() {
			System.out.println("hello Thread");
		}
	};

方法四:匿名内部类创建Runnable子类对象

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

方法五:lambda表达式创建Runnable子类对象

	Thread t1 = new Thread(() -> System.out.println("hello Thread"));
	Thread t2 = new Thread(() -> {
		System.out.println("hello Thread");
	});

三、多线程并发执行简单演示

public class Demo {
    public static void main(String[] args) {
        //thread 线程
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("thread线程执行中!");
                    //为了让这里的打印慢一点使效果更加明显,可以使用sleep方法设定线程睡眠时间
                    try {
                        Thread.sleep(1000);//每执行一次循环就睡眠1秒
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        thread.start();

        //main 线程
        for (int i = 0; i < 10; i++) {
            System.out.println("main线程执行中!");
            //为了使效果更加明显,可以使用sleep方法设定线程睡眠时间
            try {
                Thread.sleep(1000);//每执行一次循环就睡眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

在这里插入图片描述

发现结果中thread线程和main线程打印顺序是不固定的,这里究其更本在于操作系统调度线程的时候, 线程是 “抢占式执行” 的, 具体哪个线程先执行, 哪个线程后执行是不确定的, 取决于操作系统调度器的具体实现策略.

四、多线程并发执行的优势

可以观察多线程在一些场合下是可以提高程序的整体运行效率的。

  • 使用 System.nanoTime() 可以记录当前系统的 纳秒 级时间戳.
  • serial 串行的完成一系列运算. concurrency 使用两个线程并行的完成同样的运算.
  • 假设当前有两个变量, 需要把两个变量各自自增 1000w 次(典型的 CPU 密集型的场景), 可以一个线程, 先针对 a 自增, 然后再针对 b 自增; 还可以两个线程, 分别对 a 和 b 自增; 我们来看看使用两个线程和单独使用一个线程分别所需的时间是多少.

    public class Demo {
        public static void main(String[] args) {
            //多线程
            concurrency();
            //单线程
            serial();
        }
    
        // 串行执行, 一个线程完成
        public static void serial() {
            // currentTimeMillis 获取到当前系统的 ms 级时间戳.
            long beg = System.currentTimeMillis();
    
            long a = 0;
            for (long i = 0; i < 100_0000_0000L; i++) {
                a++;
            }
            long b = 0;
            for (long i = 0; i < 100_0000_0000L; i++) {
                b++;
            }
    
            long end = System.currentTimeMillis();
            System.out.println("单线程执行时间: " + (end - beg) + " ms");
        }
    
        public static void concurrency() {
            // 使用两个线程分别完成自增.
            Thread t1 = new Thread(() -> {
                long a = 0;
                for(long i = 0; i < 100_0000_0000L; i++) {
                    a++;
                }
            });
            Thread t2 = new Thread(() -> {
                long b = 0;
                for(long i = 0; i < 100_0000_0000L; i++) {
                    b++;
                }
            });
            // 记录开始执行的时间戳
            long beg = System.currentTimeMillis();
            t1.start();
            t2.start();
    
            try {
                // 等待两个线程结束后,再获取结束时的时间戳
                t1.join();
                t2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            // 获取执行结束时的时间戳
            long end = System.currentTimeMillis();
            System.out.println("多线程执行时间: " + (end - beg) + " ms");
        }
    }
    
    

    在这里插入图片描述
    多线程在IO密集型的任务中, 也是有作用的, 日常使用一些程序,经常会看到"程序未响应", 这是因为程序进行了一些耗时的IO操作(比如一些程序启动要加载数据文件,就涉及到大量的读硬盘操作), 阻塞了界面的响应, 这种情况下使用多线程也是可以有效改善的(让一个线程负责IO, 另一个线程用来响应用户的操作).

    五、Thread的常见构造方法和属性

    5.1 属性

    在这里插入图片描述
    关于java线程的属性, 我们可以通过java官方的jconsole调试工具查看java线程的一些属性, 这个工具一般在jdk的bin目录下.
    在这里插入图片描述
    双击打开出现如下界面, 选择需要查看的进程连接查看
    在这里插入图片描述
    在这里插入图片描述
    选择需要查看的进程属性查看:
    在这里插入图片描述

    5.2 方法

    在这里插入图片描述
    之前我们已经看到了如何通过覆写 run 方法创建一个线程对象,但线程对象被创建出来并不意味着线程
    就开始运行了。
    覆写 run 方法是提供给线程要做的事情的指令清单
    线程对象可以认为是把 李四、王五叫过来了
    而调用 start() 方法,就是喊一声:”行动起来!“,线程才真正独立去执行了。
    调用 start 方法, 才真的在操作系统的底层创建出一个线程.

    线程调用start和直接调用run的区别

    run 单纯的只是一个普通的方法, 描述了任务的内容。

    start 则是一个特殊的方法, 内部会在系统中创建线程。
    直接通过线程对象调用run方法就是单纯地调用了Thread对象中的一个普通方法而已, 并没有创建一个新线程来执行run方法, 而是通过main线程来执行的run方法, 而使用start方法, 会创建一个新线程并执行run方法.

    六、中断线程

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

    目前常见的有以下两种方式:

    1. 通过共享的标记来进行沟通
    2. 调用 interrupt() 方法来通知

    方法一:自己设置一个标志位来来控制线程是否要停止

    public class Demo {
        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();
    
            Thread.sleep(3000);
            // 在主线程里就可以随时通过 flag 变量的取值, 来操作 t 线程是否结束.
            flag = false;
        }
    }
    
    

    方法二:使用Thread类中自带标志位

    public class Demo {
        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.start();
            Thread.sleep(3000);
    
            t.interrupt();
        }
    }
    
    

    thread 收到通知的方式有两种:

    1. 如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通
      知,清除中断标志

    当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择
    忽略这个异常, 也可以跳出循环结束线程.
    2. 否则,只是内部的一个中断标志被设置,thread 可以通过

    Thread.interrupted() 判断当前线程的中断标志被设置,清除中断标志

    Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志
    这种方式通知收到的更及时,即使线程正在 sleep 也可以马上收到。

    七、线程等待

    有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。例如,张三只有等李四转
    账成功,才决定是否存钱,这时我们需要一个方法明确等待线程的结束。

    public class Demo {
        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();
            // 此处的 join 就是让当前的 main 线程来等待 t 线程执行结束 (等待 t 的 run 执行完)
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t线程执行完之后再执行这里!");
        }
    }
    
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/149473.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode二叉树经典题目(六):特殊位置构造二叉树

目录 21. LeetCode404. 左叶子之和 22.LeetCode513. 找树左下角的值 23. LeetCode112. 路径总和 24. LeetCode113. 路径总和 II 25. LeetCode106. 从中序与后序遍历序列构造二叉树 26. LeetCode105. 从前序与中序遍历序列构造二叉树​编辑 27. LeetCode654. 最大二叉树 …

LED、Mini LED、Micro LED、LCD、OLED技术

1、传统led、miniled、microled的异同 2、OLED OLED&#xff08;Organic Light-Emitting Diode&#xff09;&#xff0c;又称为有机电激光显示、有机发光半导体&#xff08;Organic Electroluminescence Display&#xff0c;OLED&#xff09;。OLED属于一种电流型的有机发光器…

S32K144-hello_word点灯

官方提供了很多的参考例程&#xff0c;每个历程分别配置了不同的外设&#xff0c;这里挨个尝试解读一下。 示例效果 RGB红灯绿灯交替闪烁。 导入示例 示例文件所在目录&#xff1a; 该示例使用PCC和PORT模块交替切换两个LED。 硬件连接 配置引脚功能 生成代码 S32DS自带引…

C#上位机基础学习_登录窗体的创建方法和步骤

C#上位机基础学习_登录窗体的创建方法和步骤 本次和大家分享如何制作一个简单的登录窗体。具体的方法和步骤可以参考以下内容: 如下图所示,打开Visual Studio 2019,新建一个Windows 窗体应用(.NET Framework), 如下图所示,在窗体中添加Label标签、Text文本框、Button按…

Java中常用API总结(5)—— Object类中的深克隆和浅克隆

对象克隆一、前言二、浅克隆1.概述2.实例1️⃣思路2️⃣继承cloneable接口底层原理3️⃣重写clone方法底层原理3.代码实现三、深克隆1.概述2.实例3.代码实现四、结语一、前言 本文将详细讲述Object类中的对象克隆方法&#xff0c;其中包含深克隆和浅克隆&#xff0c;两者有一定…

算法竞赛100天第2天——STL IN C++(算法竞赛必备知识总结汇总)

本文已收录于专栏 &#x1f332;《百日算法竞赛》&#x1f332; 目录 前言&#xff1a; 序列容器 序列的要求&#xff1a; 1.vector vector常用方法 vector遍历 2、deque 头文件 方法 3、list 头文件 方法 4、queue 方法&#xff1a; 关联容器 1、map 2、se…

SWPUCTF 2022新生赛部分wp

&#x1f60b;大家好&#xff0c;我是YAy_17&#xff0c;是一枚爱好网安的小白。 本人水平有限&#xff0c;欢迎各位大佬指点&#xff0c;一起学习&#x1f497;&#xff0c;一起进步⭐️。⭐️此后如竟没有炬火&#xff0c;我便是唯一的光。⭐️[SWPUCTF 2022 新生赛]ez_ez_ph…

【算法】链表

❤️ Author&#xff1a; 老九 ☕️ 个人博客&#xff1a;老九的CSDN博客 &#x1f64f; 个人名言&#xff1a;不可控之事 乐观面对 &#x1f60d; 系列专栏&#xff1a; 文章目录链表数组转链表链表转数组往链表的前面增加一个节点获得指定位置的值在链表的末尾增加一个节点在…

我最近发现的一些问题。

‍‍ 大家好&#xff0c;我是小z&#xff0c;也可以叫我阿粥~这段时间看了不少分析报告&#xff0c;启发颇多。也发现有一些普遍存在且很容易误导分析师的问题。在聊问题之前&#xff0c;先给大家分享一个&#xff08;我刚写的&#xff09;小故事&#xff1a;有一个学生…

Pytorch 多项式拟合

目录 1、训练误差和泛化误差 2、独立同分布假设 3、欠拟合和过拟合 4、多项式回归 1、训练误差和泛化误差 训练误差&#xff08;training error&#xff09;是指&#xff0c; 模型在训练数据集上计算得到的误差。 泛化误差&#xff08;generalization error&#xff09;是指…

【OpenCV 例程 300篇】255.OpenCV 实现图像拼接

『youcans 的 OpenCV 例程300篇 - 总目录』 【youcans 的 OpenCV 例程 300篇】255.OpenCV 实现图像拼接 6.2 OpenCV 实现图像拼接 OpenCV中图像的数据结构是Numpy数组&#xff0c;使用切片方法可以实现图像的裁剪&#xff0c;使用数组堆叠方法可以实现图像的拼接。 Numpy 函数…

K8S Replication Controller 示例

K8S Replication Controller Replication Controller可确保在任何时间运行指定数量的pod副本。换句话说&#xff0c;ReplicationController确保一个pod或一组同类pod始终处于可用状态。 可以理解为Replication Controller &#xff08;RC&#xff09;是基于 K8S Pod对象之上的…

PR采购申请启用灵活工作流配置

目录 1. 检查系统基础工作流配置 2. 激活标准场景模版 WS02000458 3. 维护收件箱操作按钮文本 4. 维护任务代理及激活事件 5. 配置Inbox审批页面 6. PR灵活工作流启动配置 7. 为流程设置代理人 8. 配置工作流场景 9. 可扩展部分 官方Help文档路径&#xff1a; SAP He…

算法_杨氏矩阵_杨氏矩阵算法_剑指offer

目录 一、问题描述 二、问题分析 三、算法设计 ​四、代码实现 一、问题描述 有一个数字矩阵&#xff0c;矩阵的每行从左到右是递增的&#xff0c;矩阵从上到下是递增的&#xff0c;请编写程序在这样的矩阵中查找某个数字是否存在。 要求&#xff1a;时间复杂度小于O(N);…

【目标检测】Cascade RCNN中的两个常见问题

一、关于mismatch问题 在training阶段和inference阶段使用不同的阈值很容易导致mismatch&#xff0c;什么意思呢&#xff1f; 在training阶段&#xff0c;由于给定了GT&#xff0c;所以可以把与GT的IoU大于阈值的proposals作为正样本&#xff0c;这些正样本参与之后的bbox回归…

【C语言】深入浅出讲解函数栈帧(超详解 | 建议收藏)

&#x1f680;write in front&#x1f680; &#x1f4dd;个人主页&#xff1a;认真写博客的夏目浅石. &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f4e3;系列专栏&#xff1a;凡人修C传 &#x1f4ac;总结&#xff1a;希望你看完之后&…

基于机器学习与协同过滤的图书管理推荐系统

基于机器学习与协同过滤的图书推荐系统 一、系统结构图 二、Demo示例 完整源码可联系博主微信【1257309054】 点我跳转 三、K-means聚类机器学习推荐算法 1、原理 从数据库中 1、首先获取书籍类别 2、获取用户注册时勾选喜欢的类别&#xff0c;勾选的为1&#xff0c;否则…

让 Java Agent 在 Dragonwell 上更好用

本文是《容器中的Java》系列文章之 3/n&#xff0c;欢迎关注后续连载 &#x1f603; 。1 背景 随着越来越多的云原生微服务应用的大规模部署&#xff0c;大家对微服务治理的能力需求越来越强。 Java Agent技术能够让业务专注于业务逻辑&#xff0c;与此同时&#xff0c;中间…

kali渗透测试到底该如何学?

1、渗透测试是什么&#xff1f; 渗透测试&#xff0c;是为了证明网络防御按照预期计划正常运行而提供的一种机制。渗透测试是通过各种手段对目标进行一次渗透&#xff08;攻击&#xff09;&#xff0c;通过渗透来测试目标的安全防护能力和安全防护意识。打个比方&#xff1a;比…

手写spring12(把aop动态代理整合到spring生命周期)

文章目录目标设计项目结构四、实现1、定义Advice拦截器链2、定义Advisor访问者3、方法前置拦截器——MethodBeforeAdviceInterceptor4、代理工厂——ProxyFactory5、融入Bean生命周期的自动代理创建者——InstantiationAwareBeanPostProcessor 、DefaultAdvisorAutoProxyCreato…