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

news2024/11/25 0:28:28

前言


        到目前为止,我们学到的都是有关 “顺序” 编程的知识,即程序中所有事物在任意时刻都只能执行一个步骤。例如:在我们的 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{
    @Override
    public 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 {
    @Override
    public 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() {
                @Override
                public 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();
    }

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

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

相关文章

YOLO11改进|卷积篇|引入SPDConv

目录 一、【SPD】卷积1.1【SPD】卷积介绍1.2【SPD】核心代码 二、添加【SPD】卷积2.1STEP12.2STEP22.3STEP32.4STEP4 三、yaml文件与运行3.1yaml文件3.2运行成功截图 一、【SPD】卷积 1.1【SPD】卷积介绍 SPD-Conv卷积的结构图如下&#xff0c;下面我们简单分析一下其处理过程…

贪心算法.

序幕 贪心算法&#xff08;Greedy Algorithm&#xff09;是一种在求解问题时采取逐步构建解决方案的策略&#xff0c;每一步都选择当前状态下局部最优的解&#xff0c;期望通过局部最优解能够得到全局最优解。 以上为了严谨性&#xff0c;引用了官方用语。 而用大白话总结就是&…

如何移除 iPhone 上的网络锁?本文筛选了一些适合您的工具

您是否对 iPhone 运营商的网络感到困惑&#xff1f;不用担心&#xff0c;我们将向您介绍 8 大免费 iPhone 解锁服务。这些工具可以帮助您移除 iPhone 上的网络锁&#xff0c;并使您能够永久在网络上使用您的设备。如果您想免费解锁 iPhone&#xff0c;请阅读本文并找到最适合您…

APC论文总结

论文详情 论文标题&#xff1a;APC: Adaptive Patch Contrast for Weakly Supervised Semantic Segmentation 论文作者&#xff1a;Wangyu Wu&#xff0c;Tianhong Dai&#xff0c;Zhenhong Chen&#xff0c;Xiaowei Huang&#xff0c;Fei Ma&#xff0c;Jimin Xiao 发表时间…

股票期货高频数据获取方法

导语&#xff1a;在量化交易领域&#xff0c;获取高质量的股票期货高频数据是进行有效分析和策略开发的基础。本文从专业量化角度出发&#xff0c;介绍了几种获取股票期货高频数据的方法。一、使用专业数据供应商1. 【推荐】**银河数据库&#xff08;yinhedata.com&#xff09;…

【Blender Python】6.修改物体模式

概述 Blender对象共有6种编辑模式&#xff0c;物体模式、编辑模式、雕刻模式、顶点绘制、权重绘制和纹理绘制。 在Blender Python中通过bpy.ops.object的mode_set()方法可以修改物体的编辑模式。只需要传入相应的mode参数就行了。 让物体进入编辑模式 >>> bpy.ops.…

leetcode 力扣算法题 快慢指针 双指针 19.删除链表的倒数第n个结点

删除链表的倒数第N个结点 题目要求题目示例解题思路从题目中的已知出发思考寻找目标结点条件转换核心思路 需要注意的点改进建议 完整代码提交结果 题目要求 给你一个链表&#xff0c;删除链表的倒数第n个结点&#xff0c;并且返回链表的头结点。 题目示例 示例 1&#xff1…

libcurl网络协议库使用Demo

目录 1 libcurl简介 2 libcurl编译 3 使用步骤 4 函数说明 4.1 全局初始化函数 curl_global_init 4.2 全局释放函数 curl_global_cleanup 4.3 libcurl库版本 curl_version 4.4 开启会话 curl_easy_init 4.5 结束会话 curl_easy_cleanup 4.6 设置传输选项 curl_easy_se…

最新版快递小程序源码 独立版快递系统 附教程

懂得都懂&#xff0c;现在电商平台退换货量大&#xff0c;快递需求量大&#xff0c;对接物流一个单子4块到6块之间 其中间是例如润 其余的 就不说了吧 互站上买的源码 分享一下 还有个方法赚钱就是 拼多多退货自己邮寄 5块钱 运费自己填写12元 白捡7元美滋滋 源码下载&…

Vivado - JTAG to AXI Master (GPIO、HLS_IP、UART、IIC)

1. 简介 本文分享 JTAG to AXI Master IP Core 的使用教程。 此 IP 用于 AXI 接口向设计输入数据&#xff0c;或者读取数据。通过 Tcl 控制台编写命令来驱动此 IP&#xff0c;通过 JTAG 即可进行操作&#xff0c;而这个 IP 则在 AXI 端口上驱动 AXI 事务。由于这个核心没有自…

面试题之- null和undefined的区别

前言 首先undefined和null都是基本数据类型&#xff0c;这两个基本数据类型分别都只有一个值&#xff0c;就是undefined和null。 undefined代表的含义是未定义&#xff0c;null代表的的含义是空对象&#xff0c;一般变量声明了但是还有没有定义的时候会返回undefined&#xf…

每日学习一个数据结构-默克尔树(Merkle Tree)

文章目录 概述特征构建过程使用场景示例总结 设计目的一、提高数据验证效率二、增强数据安全性三、适用于分布式系统 底层原理一、数据块划分与哈希计算二、二叉树的构建三、默克尔树的应用与优势 更新机制 概述 默克尔树&#xff08;Merkle Tree&#xff09;&#xff0c;也称…

cnn突破六(四层bpnet网络公式)

四层bpnet网络反向传播公式推导&#xff1a; X【196】-》HI【128】/HO【128】-》H2I【60】/H2O【60】-》YI【10】/YO【10】&#xff0c; 期望是d【10】 X&#xff0c;HI之间用w1【196&#xff0c;128】 HO&#xff0c;H2I之间用w12【128,60】 H2O&#xff0c;YI之间用w2【…

Zabbix 企业级应用(Zabbix Enterprise Application)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

【AI学习】Mamba学习(三):离散化SSM的矩阵计算

SSM离散化表示 除了连续的输入之外&#xff0c;还会通常碰到离散的输入(如文本序列)。所以SSM需要离散化形式&#xff0c;就是下面公式2和3。 SSM离散化过程 但是好奇这个离散化过程是如何进行的&#xff1f; 《一文通透想颠覆Transformer的Mamba&#xff1a;从SSM、HiPPO、…

【NIO基础】NIO(非阻塞 I/O)和 IO(传统 I/O)的区别,以及 NIO 的三大组件详解

目录 1、NIO 2、NIO 和 IO 的区别 1. 阻塞 vs 非阻塞 2. 一个线程 vs 多个连接 3. 面向流 vs 面向缓冲 4. 多路复用 3、Channel & Buffer (1&#xff09;Channel&#xff1a;双向通道 (2&#xff09;Buffer&#xff1a;缓冲区 (3&#xff09;ByteBuffer&#xff…

GO网络编程(五):海量用户通信系统3:整体框架与C/S通信总体流程【重要】

这个系统其实是尚硅谷的老韩讲的&#xff08;尚硅谷网络编程项目&#xff09;&#xff0c;但是他讲得很碎片化&#xff0c;思路不够清晰&#xff0c;时间又长&#xff0c;所以要掌握还是挺难的。如果你听了他的视频&#xff0c;不去梳理系统业务流程&#xff0c;不去看代码就往…

云计算身份认证与访问控制(Cloud Computing Identity Authentication and Access Control)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

支持向量机(SVM)基础教程

一、引言 支持向量机&#xff08;Support Vector Machine&#xff0c;简称SVM&#xff09;是一种高效的监督学习算法&#xff0c;广泛应用 于分类和回归分析。SVM以其强大的泛化能力、简洁的数学形式和优秀的分类效果而备受机器学 习领域的青睐。 二、SVM基本原理 2.1 最大间…

watch命令:周期执行指定命令

一、命令简介 ​watch ​命令用于周期性地执行指定的命令&#xff0c;并显示其输出结果。 ‍ 二、命令参数 2.1 命令格式 watch [选项] 命令2.2 选项 ​-n, --interval​: 指定更新间隔时间&#xff08;以秒为单位&#xff09;。默认间隔时间为 2 秒。​-d, --difference…