当前位置: 首页 > news >正文

【Java 并发编程】初识多线程

前言


        到目前为止,我们学到的都是有关 “顺序” 编程的知识,即程序中所有事物在任意时刻都只能执行一个步骤。例如:在我们的 main 方法中,都是多个操作以 “从上至下” 的顺序调用方法以至结束的。

        虽然 “顺序” 编程能够解决相当大的一部分问题,但是当所有的方法全部推在一个 “队列” 里面,如果前面的方法没有执行完,后面的方法就轮不到。这样程序的运行效率必然十分低下。对于一些问题,能够同时执行程序的多个部分,就会十分方便且必要。而并发编程的设计就是为了专门解决这一类问题。


前期回顾:Java 之深入理解 String、StringBuilder、StringBuffer

文章代码:码云地址


目录

前言

并发与并行的简单区别

 并发

总结

并行

总结

进程与线程的简单区别

进程

线程

进程与线程的区别总结

多线程的理解

创建线程任务的方式

继承 Thread 类

实现 Runnable 接⼝

匿名内部类创建

lambda 表达式创建

并发与并行的简单区别

 并发

        关于并发这里举个例子:一家餐厅来了三个食客,但是厨师只有一位,怎么出餐就成了问题。<1> 按食客进店的先后顺序上(之前提到的顺序编程),那么后两位食客等的时间久了自然不乐意肯定会掀桌子。<2> 三个人的菜同时做,虽然只有一个厨师(一个CPU核心),但是有三口锅(三个线程),当一个菜烧制的差不多的时候立刻就切换到另一个菜。这样几乎可以做到三个菜的同时出餐。

        

        在计算机中,由于 CPU 的运算速度非常的快,切换的速度也非常的快,如上图,可能执行到QQ的指令2就立刻跳转到微信的线程,站在宏观角度,可以看成是同时执行(并发执行)的。

总结

        并发是指两个或多个任务在同一时间间隔内发生比如在单核CPU上运行 16 个线程,由于核数限制,这 16 个线程无法在同一时刻运行,所以CPU只能采用时间片切换的方式来运行。

并行

          关于并行这里举个例子:一家餐厅来了三个食客,但是厨师刚好有三位。这样怎么出餐就不成问题了。我们只需每桌安排一个厨师,就可完成出餐任务。

        在计算机中,这就意味着有一个CPU有多个核心,同一时刻的程序可以分配到多个CPU处理器上,实现多任务,并行执行

总结

        当有多个CPU核心时,在同一个时刻可以同时运行多个任务,这种方式叫并行比如,3核CPU可以同时运行3个线程。

        顺序编程是指多个操作按次序执行,并行编程是指多个操作同时执行,而并发编程将同一个操作分解为多个部分并允许无序执行。

进程与线程的简单区别

进程

        进程是指正在运行中的程序实例。每个进程都是一个独立的执行单元,拥有自己的内存空间和系统资源。一个进程可以包含多个线程,是程序执行的基本单位一个在内存中运行的应用程序。比如在 Windows 系统中,一个运行的 xx.exe 就是一个进程。我们打开任务管理器就可以看到此时电脑的进程。

线程

        线程是进程内可计划执行的实体。 进程的所有线程共享其虚拟地址空间和系统资源。一个进程至少有一个线程,即主线程 main;一个进程可以运行多个线程,多个线程可共享数据。

        与进程不同的是同类的多个线程共享进程的方法区资源,但每个线程有自己的程序计数器虚拟机栈本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程

进程与线程的区别总结


        线程具有许多传统进程所具有的特征,故又称为轻型进程或进程元;而把传统的进程称为重型进程,它相当于只有一个线程的任务。在引入了线程的操作系统中,通常一个进程都有若干个线程,至少包含一个线程。

根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位

资源开销:每个进程都有独立的代码和数据空间,程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器,线程之间切换的开销小。

包含关系:如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程
内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的
影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行。

多线程的理解

这里先举个例子 ~

单进程

        猴子吃桃, 一只猴子在一间包间内需要吃一百个桃,我们知道这其实效率是很低的。那么我们可以将猴子吃桃当成是一个进程处理一个大型项目,那么这样搞会导致程序运行卡顿。所以我们需要将其优化。

多进程

        我们可以将吃完 100 颗桃子的任务分给4子猴子完成,虽然效率提升了,但是每只猴子都需要开一间包间吃桃子,这就导致了资源的开销大。将一个程序交给4个进程也是一样的道理。

多线程

        那么我在一个包间让多个猴子完成吃完桃子的计划,这样的安排是最合理的。既保证了效率,也节省了空间。那么猴子是不是越多效率越快呢?其实并不然,因为座位有限,当座位无法容纳更多的猴子,此时再添加猴子只会让它们相互 “争夺” 吃桃子的位置,从而影响吃桃的效率。

        多线程也是如此:一个进程中可以拥有很多线程来辅助程序的运行,这里提升效率的因素是 “并行”。但是如果线程太多超过了CPU的核心数目,此时就无法完成所有线程的并行执行,就会势必会造成严重的 “线程冲突”。

创建线程任务的方式


继承 Thread 类

        线程可以驱动任务,因此你需要一种描述任务的方式。这可以继承 Thread 这个线程类。要想定义任务,我们只需要重写 Thread 类中的 run() 方法即可,使得该任务执行你的命令:

class MyThread extends Thread{@Overridepublic void run() {while(true){// 即将创建出的线程,要执行的逻辑System.out.println("Hello");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}public class Demo1{public static void main(String[] args) {MyThread thread = new MyThread();// 创建线程thread.start();while(true){System.out.println("World");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

运行截图:

        <1> 任务中的 run() 方法通常总会以某种方式的循环,使得任务一直执行下去直到不需要,所以需要设置循环条件。通常 run() 会被写成无限循环形式,这就意味着,除非有某个条件使得 run() 停止,否则它将会永远的运行下去。

        <2> 由于该程序是死循环,为了更好的观察加上了sleep。而我们要捕获 sleep 方法有可能会抛出 InterruptedException 异常。一般来说,中断请求终止一个线程。相应的,如果抛出 InterruptedException 时,run() 方法就会退出。

        <3>

World
Hello
Hello
World
Hello
World
Hello
World
...

        通过打印结果我们可以发现,这个线程的输出是交错的,说明它们在并发的进行。

        <4> 以上的程序一共有两个线程,一个是 main 线程,一个是我们用 Thread 创建的线程。但是如果我们直接调用 Thread 的 run() 的方法,只会在 main 线程中执行这个方法,而没有开辟新的线程。

        <5> 虽然以上方法可以创建线程但是不推荐使用,我们应该将并行运行的任务与运行机制解耦合。如果有多个任务,为每个任务分别创建一个线程开销太大。

实现 Runnable 接⼝

class MyRunnable implements Runnable {@Overridepublic void run() {// 线程需要完成的逻辑while(true){System.out.println("Hello");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}};
}public class Demo2 {public static void main(String[] args) {// 创建 Thread 类实例, 调⽤ Thread 的构造⽅法时将 Runnable 对象作为参数MyRunnable myRunnable = new MyRunnable();Thread t1 = new Thread(myRunnable);t1.start();while (true) {System.out.println("World");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

匿名内部类创建

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

 

lambda 表达式创建

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

相关文章:

【Java 并发编程】初识多线程

前言 到目前为止&#xff0c;我们学到的都是有关 “顺序” 编程的知识&#xff0c;即程序中所有事物在任意时刻都只能执行一个步骤。例如&#xff1a;在我们的 main 方法中&#xff0c;都是多个操作以 “从上至下” 的顺序调用方法以至结束的。 虽然 “顺序” 编程能够解决相当…...

Linux下载安装MySQL8.4

这里写目录标题 一、准备工作查看系统环境查看系统架构卸载已安装的版本 二、下载MySQL安装包官网地址 三、安装过程上传到服务器目录解压缩&#xff0c;设置目录及权限配置my.cnf文件初始化数据库配置MySQL开放端口 一、准备工作 查看系统环境 确认Linux系统的版本和架构&am…...

强化学习笔记之【DDPG算法】

强化学习笔记之【DDPG算法】 文章目录 强化学习笔记之【DDPG算法】前言&#xff1a;原论文伪代码DDPG算法DDPG 中的四个网络代码核心更新公式 前言&#xff1a; 本文为强化学习笔记第二篇&#xff0c;第一篇讲的是Q-learning和DQN 就是因为DDPG引入了Actor-Critic模型&#x…...

c++继承(下)

c继承&#xff08;下&#xff09; &#xff08;1&#xff09;继承与友元&#xff08;2&#xff09;继承与静态成员&#xff08;3&#xff09;多继承及其菱形继承问题3.1 继承模型3.2 虚继承3.3 多继承中指针偏移问题 &#xff08;4&#xff09;继承和组合&#xff08;9&#xf…...

数据结构 ——— 单链表oj题:反转链表

目录 题目要求 手搓一个简易链表 代码实现 题目要求 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表 手搓一个简易链表 代码演示&#xff1a; struct ListNode* n1 (struct ListNode*)malloc(sizeof(struct ListNode)); assert(n1);…...

前端项目npm install报错解决的解决办法

报错问题一: [rootspug-api spug_web]# npm install npm WARN deprecated xterm4.19.0: This package is now deprecated. Move to xterm/xterm instead. npm WARN deprecated workbox-google-analytics4.3.1: It is not compatible with newer versions of GA starting with v…...

vue双向绑定/小程序双向绑定区别

Vue双向绑定与小程序双向绑定在实现方式、语法差异以及功能特性上均存在显著区别。以下是对这两者的详细比较&#xff1a; 一、实现方式 Vue双向绑定 Vue的双向绑定主要通过其响应式数据系统实现。Vue使用Object.defineProperty()方法&#xff08;或在Vue 3中使用Proxy对象&am…...

华为OD机试真题---字符串变换最小字符串

题目描述: 给定一个字符串s&#xff0c;最多只能进行一次变换&#xff0c;返回变换后能得到的最小字符串(按照字典序进行比较)。 变换规则: 交换字符串中任意两个不同位置的字符。 输入描述: 一串小写字母组成的字符串s 输出描述: 按照要求进行变换得到的最小字符串 补…...

JAVA基础面试题汇总(持续更新)

1、精确运算场景使用浮点型运算问题 精确运算场景&#xff08;如金融领域计算应计利息&#xff09;计算数字&#xff0c;使用浮点型&#xff0c;由于精度丢失问题&#xff0c;会导致计算后的结果和预期不一致&#xff0c;使用Bigdecimal类型解决此问题&#xff0c;示例代码如下…...

设计模式-创建型-常用:单例模式、工厂模式、建造者模式

单例模式 概念 一个类只允许创建一个对象&#xff08;或实例&#xff09;&#xff0c;那这个类就是单例类&#xff0c;这种设计模式就叫做单例模式。对于一些类&#xff0c;创建和销毁比较复杂&#xff0c;如果每次使用都创建一个对象会很耗费性能&#xff0c;因此可以把它设…...

【数据结构】【链表代码】随机链表的复制

/*** Definition for a Node.* struct Node {* int val;* struct Node *next;* struct Node *random;* };*/typedef struct Node Node; struct Node* copyRandomList(struct Node* head) {if(headNULL)return NULL;//1.拷贝结点&#xff0c;连接到原结点的后面Node…...

Linux 系统五种帮助命令的使用

Linux 系统五种帮助命令的使用 本文将介绍 Linux 系统中常用的帮助命令&#xff0c;包括 man、–help、whatis、apropos 和 info 命令。这些命令对于新手和有经验的用户来说&#xff0c;都是查找命令信息、理解命令功能的有力工具。 文章目录 Linux 系统五种帮助命令的使用一…...

Vueron引领未来出行:2026年ADAS激光雷达解决方案上市路线图深度剖析

Vueron ADAS激光雷达解决方案路线图分析&#xff1a;2026年上市展望 Vueron近期发布的ADAS激光雷达解决方案路线图&#xff0c;标志着该公司在自动驾驶技术领域迈出了重要一步。该路线图以2026年上市为目标&#xff0c;彰显了Vueron对未来市场趋势的精准把握和对技术创新的坚定…...

Java | Leetcode java题解之第458题可怜的小猪

题目&#xff1a; 题解&#xff1a; class Solution {public int poorPigs(int buckets, int minutesToDie, int minutesToTest) {if (buckets 1) {return 0;}int[][] combinations new int[buckets 1][buckets 1];combinations[0][0] 1;int iterations minutesToTest /…...

怎么不改变视频大小的情况下,修改视频的时长

视频文件太大怎么变小&#xff1f;不影响画质的四种方法 怎么不改变视频大小的情况下,修改视频的时长 截取结尾的时间你可以使用 ffmpeg 来裁剪视频的结尾部分。假设你想去掉视频最后的3秒钟&#xff0c;可以先使用 ffmpeg 获取视频的总时长&#xff0c;然后通过指定一个新的…...

数据结构:AVL树

前言 学习了普通二叉树&#xff0c;发现普通二叉树作用不大&#xff0c;于是我们学习了搜索二叉树&#xff0c;给二叉树新增了搜索、排序、去重等特性&#xff0c; 但是&#xff0c;在极端情况下搜索二叉树会退化成单边树&#xff0c;搜索的时间复杂度达到了O(N)&#xff0c;这…...

系统守护者:使用PyCharm与Python实现关键硬件状态的实时监控

目录 前言 系统准备 软件下载与安装 安装相关库 程序准备 主体程序 更改后的程序&#xff1a; 编写.NET程序 前言 在现代生活中&#xff0c;电脑作为核心工具&#xff0c;其性能和稳定性的维护至关重要。为确保电脑高效运行&#xff0c;我们不仅需关注软件优化&#xf…...

【工作流引擎集成】springboot+Vue+activiti+mysql带工作流集成系统,直接用于业务开发,流程设计,工作流审批,会签

前言 activiti工作流引擎项目&#xff0c;企业erp、oa、hr、crm等企事业办公系统轻松落地&#xff0c;一套完整并且实际运用在多套项目中的案例&#xff0c;满足日常业务流程审批需求。 一、项目形式 springbootvueactiviti集成了activiti在线编辑器&#xff0c;流行的前后端…...

SumatraPDF一打开就无响应怎么办?

结论&#xff1a;当前安装版不论32位还是64位都会出现问题。使用portable免安装版未发现相关问题。——sumatrapdf可以用于pdf, epub, mobi 等格式文件的浏览。 点击看相关问题和讨论...

棋牌灯控计时计费系统软件免费试用版怎么下载 佳易王计时收银管理系统操作教程

一、前言 【试用版软件下载&#xff0c;可以点击本文章最下方官网卡片】 棋牌灯控计时计费系统软件免费试用版怎么下载 佳易王计时收银管理系统操作教程 棋牌计时计费软件的应用也提升了顾客的服务体验&#xff0c;顾客可以清晰的看到自己的消费时间和费用。增加了消费的透明…...

Excel下拉菜单制作及选项修改

Excel下拉菜单 1、下拉菜单制作2、下拉菜单修改 下拉框&#xff08;选项菜单&#xff09;是十分常见的功能。Excel支持下拉框制作&#xff0c;通过预设选项进行菜单选择&#xff0c;可以避免手动输入错误和重复工作&#xff0c;提升数据输入的准确性和效率 1、下拉菜单制作 步…...

树莓派 mysql (兼容mariadb)登陆问题

树莓派 mysql &#xff08;兼容mariadb&#xff09;登陆问题 树莓派 MySQL 登陆问题 1 使用默认账号登陆 在首次登陆的情况下&#xff0c;系统默认为root用户授权 sudo su root ![切换到root 用户](https://img-blog.csdnimg.cn/20191019082911668.png) 2. 使用root用户登…...

智能手表(Smart Watch)项目

文章目录 前言一、智能手表&#xff08;Smart Watch&#xff09;简介二、系统组成三、软件框架四、IAP_F411 App4.1 MDK工程结构4.2 设计思路 五、Smart Watch App5.1 MDK工程结构5.2 片上外设5.3 板载驱动BSP5.4 硬件访问机制-HWDataAccess5.4.1 LVGL仿真和MDK工程的互相移植5…...

设计模式~~~

简单工厂模式(静态工厂模式) 工厂方法模式 抽象工厂角色 具体工厂角色...

Golang | Leetcode Golang题解之第458题可怜的小猪

题目&#xff1a; 题解&#xff1a; func poorPigs(buckets, minutesToDie, minutesToTest int) int {if buckets 1 {return 0}combinations : make([][]int, buckets1)for i : range combinations {combinations[i] make([]int, buckets1)}combinations[0][0] 1iterations…...

欢聚时代(BIGO)Android面试题及参考答案

网络 TCP 和 UDP 协议的区别是什么? TCP(Transmission Control Protocol,传输控制协议)和 UDP(User Datagram Protocol,用户数据报协议)是两种不同的传输层协议,它们有以下主要区别: 一、连接性 TCP 是面向连接的协议。在通信之前,需要通过三次握手建立连接,通信结束…...

[C语言]指针和数组

目录 1.数组的地址 2.通过指针访问数组 3.数组和指针的不同点 4.指针数组 1.数组的地址 数组的地址是什么&#xff1f; 看下面一组代码 #include <stdio.h> int main() { int arr[5] {5,4,3,2,1}; printf("&arr[0] %p\n", &arr[0]); printf(&qu…...

Centos Stream 9备份与恢复、实体小主机安装PVE系统、PVE安装Centos Stream 9

最近折腾小主机&#xff0c;搭建项目环境&#xff0c;记录相关步骤 数据无价&#xff0c;丢失难复 1. Centos Stream 9备份与恢复 1.1 系统备份 root权限用户执行进入根目录&#xff1a; cd /第一种方式备份命令&#xff1a; tar cvpzf backup.tgz / --exclude/proc --exclu…...

Linux的发展历史与环境

目录&#xff1a; 引言Linux的起源早期发展企业级应用移动与嵌入式系统现代计算环境中的Linux结论 引言 Linux&#xff0c;作为开源操作系统的代表&#xff0c;已经深刻影响了全球的计算环境。从其诞生之初到如今成为服务器、嵌入式系统、移动设备等多个领域的核心&#xff0c…...

Jax(Random、Numpy)常用函数

目录 Jax vmap Array reshape Random PRNGKey uniform normal split choice Numpy expand_dims linspace jax.numpy.linalg[pkg] dot matmul arange interp tile reshape Jax jit jax.jit(fun, in_shardingsUnspecifiedValue, out_shardingsUnspecifiedVa…...