【JavaEE】浅谈线程(一)

线程

  • 前言
  • 线程的由来
  • 线程是什么
    • 线程的属性
    • 线程更高效的原因
    • 举个例子(线程便利性的体现)
  • 多线程代码
    • 线程并发执行的代码
    • jconsole(观测多线程)
  • 线程的调度问题
  • 创建线程的几种方法
    • 1)通过继承Thread 重写run
    • 2)使用Runnable接口 重写run 有利于解耦合
    • 3)匿名内部类
    • 4)Runnable匿名内部类
    • 5)Lambda表达式
  • 总结


前言

线程的由来

现如今的CPU存在多个核心,因此能够同时处理多个进程,我们也称为并发执行(并行+并发)。通过这样的“多进程模式”,能够很好的利用多核CPU,极大的提升了效率。
但是,多进程的编程虽然好用,但也会带来一些问题。
在服务器中,需要给多个客户端提供服务,如果同时来了多个客户端,如此一来,只能用多个进程处理这件事情。这样会导致服务器频繁进行创建+销毁进程的操作,这是极其影响效率的。
于是引入了"轻量级进程"——线程。线程的创建和开销会小的多。

线程是什么

线程,可以理解为“线程的一部分”;一个进程中可以包含多个线程。
在进程中存在PCB结构体,一个PCB代表着一个线程,而一个进程由多个PCB构成,若干个PCB联合在一起,描述了一个进程。举个例子来说:进程像是一个小区,而线程就是一个个住在小区里的人。

线程的属性

因为线程存在与进程中,因此他们共用一个PCB,为了区分线程之间的区别,设置了一个新的概念词tgid(tgd)。同一个进程的tgid是相同的而pid是不同的;线程们共用同一份内存指针和文件描述符表。
因为线程们共用内存资源和文件资源的原因,线程之间可以访问到彼此的数据。而每个线程都是在独立的CPU上调度执行。因此我们可以认为:线程是系统调度执行的基本单位,进程是系统资源分配的基本单位。

线程更高效的原因

进程在创建和销毁过程中需要进行资源分配和资源释放的过程,而线程存在于进程中,线程之间共用资源,在线程创建和销毁的过程中可以省略资源分配/释放的步骤,省去了申请资源的过程,因此线程是更高效,更轻量级的一种进程。

举个例子(线程便利性的体现)

现在有一项任务需要完成,我们可以选择创建一个进程来解决,如果这个任务太繁重了,我们可以选择创建多个进程来分担压力。这种方法好是好,但是进程的创建和销毁会占用到资源,造成资源的浪费。
但是现在,我们学过线程了!我们可以选择在进程中创建多个线程,同时通过多核CPU的特点让线程可以达到并发执行的效果,这样既不会有申请资源的烦恼,又能高速高效的执行任务,可谓是一举多得!(鼓掌)
当然说归说,我们不能想到这么方便,直接在进程中创建多个线程叠罗汉不就好了,这肯定是不对的。俗话说的好:物极必反。如果我们一味的引入,自然会引发其他的问题(且听下回分解)

多线程代码

在JVM中已经对线程封装好了,即Thread类。我们可以通过Thread类实现线程的创建。接下来我写一份简单的代码进行演示

class MyThread extends Thread {@Overridepublic void run() {System.out.println("hello thread");}
}
public class ThreadDemo1{public static void main(String[] args) {Thread t = new MyThread();t.start();}
}

如何理解这段代码呢?首先创建了MyThread类并继承Thread方法,实现动态绑定。接着重写Thread的run方法;run()方法中包含的是线程t要执行的方法;而t.start()方法是启动线程t的方法。这段代码中有两个线程:t线程和main线程。由此我们可以知道,在学习线程之前的代码中,只存在着一个线程:main线程。同时我们也称main线程为主线程。
当然,run()是start()创建出来的线程,在线程里被调用的,run()方法已经创建好而不去手动调用,将这个方法交给系统/其他的库调用,即“回调函数”。

线程并发执行的代码

或许在上面的代码中我们不能很好的看出来t线程和main线程是怎么体现并发执行的,因此这边我分别在两个线程中加入两个循环以求更好的体现出线程并发执行的特点。

class MyThread extends Thread {@Overridepublic void run() {while (true){System.out.println("hello thread");}}
}
public class ThreadDemo1{public static void main(String[] args) {Thread t = new MyThread();t.start();while (true){System.out.println("hello main");}}
}

在这里插入图片描述
在这里我们也看到了 “hello thread” 和 "hello main"线程在控制台上交替打印,这也代表着两个线程正在并发执行。

jconsole(观测多线程)

jconsole是java的一款观测线程的实用工具,我已在其他文章中编写好,需要的可以观看下面的文章。
jconsole的简单使用

线程的调度问题

在上面的线程循环代码中我们已经可以证明多线程是并发执行的。
那么他们执行的顺序是否存在规律呢?
我们可以使用Thread.sleep()方法进行简单的观测。

Thread.sleep()可以设置时间使线程停止接下来的工作,使线程进入休眠状态(阻塞)。在规定的时间过后线程继续运行,执行未完成的任务。

代码如下

class MyThread extends Thread {@Overridepublic void run() {while (true){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
public class ThreadDemo1{public static void main(String[] args) {Thread t = new MyThread();t.start();while (true){System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

部分运行结果如下:
在这里插入图片描述
从这部分运行结果我们可以看出来,线程的运行使无序的。在操作系统中也称之为**“抢占式执行”。任何一个线程,在执行到任何一个代码的过程中,都可能被其他的线程抢占掉它的cpu资源,于是cpu就给别的线程执行了。
如果是这样的结果,显然这样的抢占式执行充满了随机性,执行效果难以预测甚至
可能引入bug。**这肯定是程序猿们不想看到的,这涉及到了线程的安全问题。

创建线程的几种方法

1)通过继承Thread 重写run

在上面的代码中已经展示,这边不再赘述。

2)使用Runnable接口 重写run 有利于解耦合

在Runnable接口中,我们可以通过查看源码看到,其中只有一个抽象类方法run(),因此Runnable的作用就是描述一个任务,一个行为。**无论是线程还是其他方法执行run()这个方法都是可以的**。

通过Runnable方法实现的多线程有一个好处:解耦合
在第一种写法中,线程重写自己的run()方法,以及线程调用的都是自己本身的方法;而使用Runnable方法之后,在后续进行代码的改动将会降低成本。

在这里插入图片描述
下面给出一个举例

class MyRunnable implements Runnable{@Overridepublic void run() {while (true){System.out.println("hello runnable");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
public class ThreadDemo2{public static void main(String[] args) {Thread t = new Thread(new MyRunnable());t.start();while (true){System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

3)匿名内部类

继承Thread 重写run,创建Thread的子类(不知名),同时也创建了一个不具名子类实例。我们看看下面的代码:

public class ThreadDemo3{public static void main(String[] args) {Thread t1 = new Thread(){@Overridepublic void run() {System.out.println("使用匿名内部类");}};t1.start();}
}

4)Runnable匿名内部类

与Thread匿名内部类的写法类似,在new Runnable之后直接重写Runnable的方法,这也创建了一个实例(不知名)实现了Runnable接口的方法。
演示代码如下:

public class ThreadDemo4 {public static void main(String[] args) {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("匿名内部类使用Runnable");}});t1.start();System.out.println("hello main");}
}

5)Lambda表达式

通过使用lambda表达式代替需要重写的run方法。

public class ThreadDemo5 {//lambda表达式//lambda本质上也是一个匿名函数,用完就丢public static void main(String[] args) {Thread t = new Thread(() ->{while (true){System.out.println("hello thread");}});t.start();System.out.println("hello main");}
}

总结

线程是一个轻量级进程,能够很大程度上节约成本和资源,在开发中有着十分重要的作用。同样的,引入多线程意味着我们需要解决一些引入所带来的问题。(如线程安全问题)这是随着我们逐渐深入了解所需要做的。
需要源码的可以点击链接:线程代码
有帮助到大家的请三连~感谢!!

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

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

相关文章

MySQL 上亿大表,如何深度优化?

背景 分析 测试 实施 索引优化后 delete大表优化为小批量删除 总结 前段时间刚入职一家公司,就遇上这事! 背景 XX实例(一主一从)xxx告警中每天凌晨在报SLA报警,该报警的意思是存在一定的主从延迟(…

【Node】Node的配置文件的使用,dotenv框架的使用

😁 作者简介:一名大四的学生,致力学习前端开发技术 ⭐️个人主页:夜宵饽饽的主页 ❔ 系列专栏:Node.js 👐学习格言:成功不是终点,失败也并非末日,最重要的是继续前进的勇…

linux-docker安装nginx

1.拉取镜像: docker pull nginx2.创建挂在路径: mkdir -p /usr/local/nginx/conf mkdir -p /usr/local/nginx/logs mkdir -p /usr/local/nginx/www mkdir -p /usr/local/nginx/conf.d 3.启动镜像:为了拿到位置文件,先启动下 docker run -…

2024 EasyRecovery易恢复 帮你轻松找回回收站删除的视频

随着数字化时代的到来,我们的生活和工作中越来越依赖于电子设备。然而,电子设备中的数据丢失问题也随之而来。数据丢失可能是由各种原因引起的,如硬盘故障、病毒感染、误删除等。面对这种情况,一个高效、可靠的数据恢复工具变得尤…

智慧农场牧场认养系统畜牧养殖积分签到直播监控农资商城养鸡APP小程序支持定制

每日签到:用户每天签到可以获取积分,连续签到7天还有惊喜奖品,这有助于增加用户粘性和活跃度。 我的鸡崽:这一功能以动画形式展示用户的鸡崽状态,新用户可以通过购物满额获得鸡苗,并通过饲喂动作参与鸡的成…

Netty NioEventLoop详解

文章目录 前言类图主要功能NioEventLoop如何实现事件循环NioEventLoop如何处理多路复用Netty如何管理Channel和Selector管理Channel管理Selector注意事项 前言 Netty通过事件循环机制(EventLoop)处理IO事件和异步任务,简单来说,就是通过一个死循环&…

【堡垒机】堡垒机的介绍

目前,常用的堡垒机有收费和开源两类。 收费的有行云管家、纽盾堡垒机; 开源的有jumpserver; 这几种各有各的优缺点,如何选择,大家可以根据实际场景来判断 什么是堡垒机 堡垒机,即在一个特定的网络环境下&…

李沐23_LeNet——自学笔记

手写的数字识别 知名度最高的数据集:MNIST 1.训练数据:50000 2.测试数据:50000 3.图像大小:28✖28 4.10类 总结 1.LeNet是早期成功的神经网络 2.先使用卷积层来学习图片空间信息 3.使用全连接层来转换到类别空间 代码实现…

【oracle数据库安装篇一】Linux5.6基于LVM安装oracle10gR2单机

说明 本篇文章主要介绍了Linux5.6基于LVM安装oracle10gR2单机的配置过程,比较详细,基本上每一个配置部分的步骤都提供了完整的脚本,安装部分都提供了简单的说明和截图,帮助你100%安装成功oracle数据库。 安装过程有不明白的地方…

二维相位解包理论算法和软件【全文翻译- DCT相位解包裹(5.3.2)】

5.3.2 基于 DCT 的方法 在本节中,我们将详细介绍如何通过 DCT 算法解决非加权最小二乘相位解缠问题,而不是通过FFT.我们将使用公式 5.53 所定义的二维余弦变换。我们开发的算法等同于 FFT 方法 2(第 5.3.1 节)。与 FFT 方法 I 等价的 DCT 算法也可以推导出来,但我们将其作…

PlayerSettings.WebGL.emscriptenArgs设置无效的问题

1)PlayerSettings.WebGL.emscriptenArgs设置无效的问题 2)多个小资源包合并为大资源包的疑问 3)AssetBundle在移动设备上丢失 4)Unity云渲染插件RenderStreaming,如何实现多用户分别有独立的操作 这是第381篇UWA技术知…

Meta 的 Llama 模型系列即将迎来第三次大更新

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

linux启动流程(s3c2400)

概述 大致流程:内核(kernel)都是由bootloader程序引导启动的,所以我们应该先烧进去bootloader程序。然后可以通过保存的内核代码或者通过远程连接(nfs/tftp)的主机下载再运行,再挂载根文件系统。…

ppt从零基础到高手【办公】

第一章:文字排版篇01演示文稿内容基密02文字操作规范03文字排版处理04复习&作业解析第二章:图形图片图表篇05图形化表达06图片艺术化07轻松玩转图表08高效工具&母版统一管理09复习&作业解析10轻松一刻-文字图形小技巧速学第三章:…

SWM341系列应用(RTC、FreeRTOS\RTTHREAD应用和Chip ID)

SWM341系列RTC应用 22.1、RTC的时钟基准 --liuzc 2023-8-17 现象:客户休眠发现RTC走的不准,睡眠2小时才走了5分钟。 分析与解决:经过排查RTC的时钟源是XTAL_32K,由于睡眠时时设置XTAL->CR0;,会把XTAL_32K给关…

C语言:指针详解(1)

目录 一、内存和地址 1.内存 2.究竟该如何理解编址 二、指针变量和地址 1.取地址操作符(&) 2.解引用操作符(*) 3.指针变量的大小 三、指针变量类型的意义 1.指针的解引用 2.指针-整数 3.void*指针 四、const修饰指针 1.const修饰变量 2.const修饰指针变量 五…

公开课学习——仿抖音直播平台

文章目录 直播抖音的直播原理Java继承直播客户端工具: ffmpeg客户端和网页集成CDN网络——性能提升关键——边缘计算 实时聊天——IM系统怎么实现?——websocketIM系统消息如何转发?直播场景IM系统是什么样子? 直播 抖音的直播原…

安全操作代码优化思路

理论依据 数据增强和样本选择 在训练阶段,您可以考虑添加数据增强来提升模型的鲁棒性和泛化能力。针对人脸检测任务,可以尝试以下改进: 对输入图像进行随机裁剪、缩放、旋转、翻转等数据增强操作,以增加数据的多样性。 使用难样…

操作系统—修改xv6内核调度算法

文章目录 修改xv6内核调度算法1.实验环境2.基于优先级的调度算法(1).基本实现思路(2).实现流程(3).一些问题 3.乐透调度算法(1).思路(2).实现流程(3).一些问题 总结参考资料 修改xv6内核调度算法 1.实验环境 这一次的实验因为是在xv6内核中实现一些调度算法,因此我…

在Linux系统上实现TCP(socket)通信

一.什么TCP TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。 二.TCP通信流程 三. TCP 服务器端 1 创建socket int sockfd socket(AF_INET, SOCK_STREAM, 0); //SOCK_STREAM tcp通信2 绑定(bind) struct sockaddr_in myad…