什么是线程?线程和进程的关系?如何创建/查看线程?

news2024/11/16 21:23:37

文章目录

  • 一. 认识线程(Thread)
    • 1.1 概念
      • 1.1.1 什么是线程
      • 1.1.2 线程存在的意义
      • 1.1.3 进程和线程之间的区别和联系
      • 1.1.4 Java的线程和操作系统的线程
    • 1.2 创建线程
      • ① 继承Thread类
      • ② 实现Runnable 接口
      • 对比两种方法
      • ③ 变形写法
      • ④ 其他写法
    • 1.3 查看线程

一. 认识线程(Thread)

1.1 概念

1.1.1 什么是线程

进程进一步细化为线程, 是程序内部的一条执行路径. 一个进程中至少有一个线程. 每个线程之间都可以按照顺讯执行自己的代码. 多个线程之间"同时"执行多份代码.

1.1.2 线程存在的意义

① “并发编程"成为"刚需”

  • 单核 CPU 的发展遇到了瓶颈. 要想提高算力, 就需要多核 CPU. 而并发编程能更充分利用多核 CPU 资源.
  • 有些人物场景需要"等待IO", 为了让等待IO的时间能去做一些其他的工作, 也需要用到并发编程.

② 虽然多进程也能实现并发编程, 但是线程比进程跟轻量.

  • 创建和销毁的速度更快
  • 调度的速度更快

③ 线程虽然比进程轻量, 但是人们还不满足, 于是又有了"线程池(ThreadPool)" 和 “协程(Coroutine)”.

1.1.3 进程和线程之间的区别和联系

  • 进程包含线程, 都是实现并发编程的方式. 每个进程至少有一个线程, 即主线程.
  • 进程和进程之间不共享内存空间,可以保持系统的稳定性; 同一个进程的线程之间共享同一个内存空间(操作系统的线程), 一旦一个线程出现异常, 可能会导致整个进程异常, 容易出现线程安全问题.
  • 一个PCB描述一个线程, 多个PCB描述一个进程. 一个进程中的所有PCB内的内存指针和文件描述符表是一样的, 但是上下文, 状态, 优先级等属性是不一样的. 这也说明了, 同一个进程中的线程共用同一份资源(内存 + 硬盘), 但是每个线程独立去CPU上调度.
  • 进程是操作系统进行资源分配的基本单位, 而线程是操作系统进行调度执行的基本单位. 创建进程的时候已经分配了资源, 后续创建进程, 直接共用之间的资源即可.

1.1.4 Java的线程和操作系统的线程

线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对用户层提供了一些 API 供用户使用.

Java中的线程是对系统级别的线程的封装和抽象.

操作系统级别下, 同一个进程的线程之间共享同一个内存空间;

而Java中的线程有私有空间.

在这里插入图片描述

1.2 创建线程

① 继承Thread类

  1. 创建一个线程类, 继承自Thread类

  2. 重写run方法: 线程入口方法, 描述线程执行的动作.

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

    MyThread t = new MyThread();
    
  4. 调用 start 方法启动线程

    t.start(); // 线程开始运行
    
    • 这个操作就会在底层调用操作系统提供"创建线程"的API, 同时就会在操作系统内核里面创建出对应的pcb结构, 并且加入到对应的链表中.
    • 此时, 这个新创建出来的线程就会参与到CPU的调度中, 这个线程接下来要执行的工作, 就是刚刚上面重写的run方法.

代码解析:

class MyThread extends Thread {
    @Override
    public void run() {
        while (true) {
            System.out.println("MyThread");
        }
   }
}
public class Main {
    MyThread t = new MyThread();
    t.start();
    while (true) {
            System.out.println("Main");
    }
}

第十行t.start()创建出了新的线程, 执行run()中的代码.

原来的主线程继续往下执行

主线程和新线程是并发执行的关系, 根据操作系统的调度执行.

注意:

  • 如果自己手动调用run()方法, 那么就只是普通方法, 没有启动多线程模式.
  • run()方法由JVM调用, 什么时候调用, 执行的时候都由操作系统的CPU调度决定.
  • 想要启动多线程, 必须调用start方法
  • 一个线程对象只能调用一次start()方法启动, 如果重复调用了, 则将抛出以上的异常"IllegalThreadStateException"

② 实现Runnable 接口

  1. 实现Runnable接口

    class MyRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println("这里是线程运行的代码");
       }
    }
    
  2. 创建Thread类的实例, 调用Thread的构造方法将Runnable对象作为参数

    Thread t = new Thread(new MyRunnable());
    
  3. 调用start方法

    t.start();
    

通过实现Runnable接口, 使得该类有了多线程的特征. 所有的分线程要执行的代码都在run方法里面.

在启动多线程的时候, 需要先通过Thread类的构造方法Thread(Runnable target)构造出对象, 然后调用Thread对象的start方法来运行多线程代码.

实际上, 所有的多线程代码都是通过运行Thread.start()来运行的, 因此, 不管是继承Thread类还是实现Runnable接口来实现多线程, 最终还是通过Thread的对象的API来控制流程的.

说明: Runnable对象仅仅作为Thread对象的target, Runnable实现类里包含的run()方法仅作为线程执行体. 而实际的线程对象依然是Thread实例, 只是该Thread线程负责执行其target的run()方法.

这一方法, 将线程要执行的任务和线程本身, 进一步解耦合了.

对比两种方法

联系:

Thread类实际上也是实现了Runnable接口的类

public class Thread extends Object implements Runnable

区别:

  • 继承Thread: 线程存放Thread子类run方法中.
  • 实现Runnable: 现成代码存放在接口的子类的run方法.

实现Runnable接口比继承Thread类所具有的优势

  • 避免了单继承的局限性
  • 多个线程可以共享同一个接口实现类的对象, 非常适合多个相同线程来处理同一份资源
  • 增加程序的健壮性, 实现解耦操作, 代码可以被多个线程共享, 代码和线程独立.

③ 变形写法

  1. 匿名内部类创建Thread子类的对象.

    public class Demo3{
        Thread t1 = new Thread() {
            @Override
            public void run() {
                System.out.println("使用匿名类创建 Thread 子类对象");
            }
        };
        t1.start();
    }
    

    说明:

    new Thread(): 创建一个子类, 继承自Thread, 这个子类是匿名的, 并且是在Demo3这个类的内部创建的.

    ② 在子类中重写了run方法

    ③ 创建了该子类的实例, 并且使用t1这个引用来指向.

  2. 匿名内部类创建Runnable子类的对象

    Thread t2 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("使用匿名类创建 Runnable 子类对象");
        }
    });
    

    说明:

    new Runnable(): 创建一个类, 实现Runnable接口.

    ② 在类中重写了run方法

    ③ 创建了该类的实例, 并把这个实例作为参数传给了Thread的构造方法.

  3. lambda表达式创建Runnable子类对象

    Thread t3 = new Thread(() -> System.out.println("使用匿名类创建 Thread 子类对象"));
    Thread t4 = new Thread(() -> {
        System.out.println("使用匿名类创建 Thread 子类对象");
    });
    

    ④ 其他写法

  • 基于Callable
  • 基于线程池

1.3 查看线程

找到当前项目使用的jdk的位置, 并双击打开

在这里插入图片描述

双击bin目录, 找到jconsole.exe

在这里插入图片描述

双击jconsole.exe, 会出现以下窗口

在这里插入图片描述

选择本地进程, 并找到当前运行的线程名称, 再点击连接

在这里插入图片描述

若出现以下窗口, 选择不安全连接.

在这里插入图片描述

出现以下窗口后, 点击线程

在这里插入图片描述

左下角就列出了所有的线程, 不仅有主线程和自己创建的线程, 还有JVM自带的线程.

在这里插入图片描述

点击某个线程, 就能得到该线程的具体信息

在这里插入图片描述

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

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

相关文章

C++之科学技术法e使用(一百七十二)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…

阿里云服务器全方位介绍_性能功能优势和租用费用

阿里云服务器全方位介绍包括云服务器ECS优势、云服务器租用价格、云服务器使用场景及限制说明,阿里云服务器网分享云服务器ECS介绍、个人和企业免费试用、云服务器活动、云服务器ECS规格、优势、功能及应用场景详细说明: 目录 什么是云服务器ECS&#…

学习笔记22 set

一、概述 Set是一种集合类型,可以快速在大量数据中查找特定值。 Set存储无序序列中的元素,并且不允许重复。与列表不同,列表中的数据可以通过索引访问,但是在集合中,元素没有与集合中的位置相关联。 Set是优化了搜索…

消息队列总结(4)- RabbitMQ Kafka RocketMQ高性能方案

1.RabbitMQ的高性能解决方案 1.1 发布确认机制 RabbitMQ提供了3种生产者发布确认的模式: 简单模式(Simple Mode):生产者发送消息后,等待服务器确认消息已经被接收。这种模式下,生产者发送消息后会阻塞&am…

M5ATOMS3基础03给ROS1发一个问候(rosserial)

引出问题 关于之前2020年的博客: 01. ESP8266和ROS调试一些问题汇总 02. ESP8266和ESP32配置(需使用ROS1和ROS2) 效果展示 使用M5ATOMS3与ROS1(kinetic,melodic,noetic)版本通信比较通用的是…

BUU [网鼎杯 2020 朱雀组]phpweb

BUU [网鼎杯 2020 朱雀组]phpweb 众生皆懒狗。打开题目,只有一个报错,不知何从下手。 翻译一下报错,data()函数:,还是没有头绪,中国有句古话说的好“遇事不决抓个包” 抓个包果然有东西,仔细一看这不就分别是函数和参…

【算法基础:贪心】6. 贪心

文章目录 区间问题905. 区间选点(排序 贪心)908. 最大不相交区间数量(排序 贪心)906. 区间分组(排序 优先队列 贪心)⭐907. 区间覆盖(排序 贪心) Huffman树148. 合并果子&#…

【LeetCode 75】第十四题(643)子数组最大平均数

题目: 示例: 分析: 给一个数组,问数组里长度为k的连续数组中的最大平均值是多少. 这题已经把意思说的很明白了,并且连子数组的长度都固定了,并且是连续的,这里可以直接使用固定长度的滑动窗口来计算. 用两个指针来在数组里划定一个长度为k的范围,然后计算指针范围内的平均数…

数组传参,指针传参

文章目录 一维数组传参二维数组传参一级指针传参二级指针传参 一维数组传参 二维数组传参 一级指针传参 二级指针传参

CentOS 8 上安装 Nginx

Nginx是一款高性能的开源Web服务器和反向代理服务器,以其轻量级和高效能而广受欢迎。在本教程中,我们将学习在 CentOS 8 操作系统上安装和配置 Nginx。 步骤 1:更新系统 在安装任何软件之前,让我们先更新系统的软件包列表和已安…

【树链剖分+MST】CF609E

Problem - E - Codeforces 题意: 思路: 先把全局的MST求出来,然后对于一条边,如果它本来就在MST中,说明代价就是MST的权值和,否则它加入MST中,此时MST形成了环,我们把环中最大的那…

深入探究Java面向对象的三大特征:封装、继承、多态

文章目录 1. 封装(Encapsulation)2. 继承(Inheritance)3. 多态(Polymorphism)结语 导语:Java是一门面向对象的编程语言,其核心思想是将现实世界中的事物抽象成对象,并通过…

Python(五十二)列表元素的判断及遍历

❤️ 专栏简介:本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中,我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 :本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

自己整理的JAVA集合

概括: 数组,链表,散列表,二分查找树,红黑树是五种不同的数据结构,它们有各自的特点和用途。ArrayList,LinkedList,HashTable,LinkedHashMap,HashMap 是 Java…

Camera组件

Clear Flags: Skybox:天空盒 Solid Color:填充颜色,当有空白处时填充背景颜色 Depth Only:只渲染想要渲染的层级 Dont Clear:不清除上一帧所留下来的数据,可以做类似残影的效果 Culling Mas…

Unity Addressable

Unity重要目录 工程中的几个重要目录 Assets存放资源、代码、配置Library大部分的资源导入到Assets目录之后,会转化成Unity认可的文件,转化后的文件会存储在这个目录Logs日志文件Packages第三方插件ProjectSettings存放各种项目设定UserSettings用户偏好…

CentOS 8 错误: Error setting up base repository

配置ip、掩码、网关、DNS VMware网关可通过如下查看 打开网络连接 配置镜像的地址 vault.centos.org/8.5.2111/BaseOS/x86_64/os/

java 阿里云 发送短信功能实现

1. 注册短信平台(以阿里云为例) 常用短信服务平台:阿里云、华为云、腾讯云、京东、梦网、乐信等 2. 注册成功后,开通短信服务 3. 设置短信签名、短信模板、AccessKey AccessKey 是访问阿里云 API 的密钥,具有账户的完全权限,我们…

C语言实现三子棋游戏

test.c源文件 - 三子棋游戏测试 game.h头文件 - 三子棋游戏函数的声明 game.c源文件 - 三子棋游戏函数的实现 主函数源文件: #define _CRT_SECURE_NO_WARNINGS 1#include"game.h" //自己定义的用"" void menu() {printf("*************…

代码随想录算法训练营day43

文章目录 Day43 最后一块石头的重量II题目思路代码 目标和题目思路代码 一和零题目思路代码 Day43 最后一块石头的重量II 1049. 最后一块石头的重量 II - 力扣(LeetCode) 题目 有一堆石头,每块石头的重量都是正整数。 每一回合&#xff0…