【javaEE-有关CPU进程和线程实现的并发编程及二者的区别】

2024-06-13 12:20

本文主要是介绍【javaEE-有关CPU进程和线程实现的并发编程及二者的区别】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

🔥🔥🔥有关进程并发编程开发的成本问题

这次之前其实我们所有的写的程序都是使用单核心来运行的,但是一般我们的计算机都有很多核心,如果我们编程的时候,只使用一个核心的话,其实这是一个非常大的浪费,所以我们引进了多进程这种并发编程来充分利用计算机的多核心。但是随着我们的需求越来越大,进程的一些缺点就显现出来了。

  • 创建和销毁进程的成本很大
  • 越来越多的需求场景需要一种更方便的并发编程形式
  • 线程的出现

由此我们发明了一个新的并发编程的形式,线程(Thread),对比进程,它的创建和销毁成本更小,更轻量。因此现在的主流并发编程的形式就是多线程编程。

在这里插入图片描述

🔥🔥🔥多线程编程

对于进程来说,一个进程是由一组PCB类似于结构体的形式对数据进行描述,进一步的通过链表的形式将数据组织起来。对于线程来说,一个线程是只有有一个PCB结构的形式,所以这里就有一个包含关系:一个进程中可以包含多个线程,此时每一个线程都可以独立的在CPU上调度执行,这里我们对进程和线程做一个总结:

  • 进程是系统资源分配的基本单位
  • 线程是cpu调度执行的基本单位

一个可执行的程序,双击exe文件,系统就会创建一个进程,给这个进程中分配一些系统资源(硬盘,cpu,内存,网络带宽等)。在这个进程中会创建一个或多个线程。这些线程才在cpu中调度执行。同一个进程中的多个线程其实是共用同一份系统资源的。

对比进程,线程是更轻量的,主要是在我们创建一个进程的时候,就已经创建了一个线程了,同时就会分配系统资源,后序我们在创建第二个第三个线程的时候,就不用再分配资源了~,因此他就省去了分配资源的过程。也省去了释放资源的过程。在这里插入图片描述

🔥🔥🔥系统分析多线程调度执行过程

比如说现在有请我们最可爱的joey老铁上场,此时在一个房间里他要一个人吃完100只鸡。
在这里插入图片描述
为了提高老铁的吃鸡效率,我们可以引用多进程的概念,在创建一个房间,还是一个joey老铁,只需要吃50只鸡。
在这里插入图片描述
这样确实提高了joey吃鸡的效率,但是我们其实不难发现这种效率是非常低的,我们显然可以用一个更简单的方式提高吃鸡的效率,我们可以在一个房间里引进多个joey老铁,这样的方式不仅大大提高了吃鸡的效率,而且大大缩减了成本的损耗。这其实就是多线程的概念。相比于多进程,这种形式效率是极高的。
在这里插入图片描述

那我们就自然的想到,在引进一些线程,会不会进一步提高效率呢?当然会!!!
在这里插入图片描述
但是当我们将线程增加带一些程度的时候,就会出现问题了~
在这里插入图片描述
此时就再也无法通过增加线程的方式,提高程序执行的效率了,因为桌子的空间是有限的,我们增加多个joey后,势必会有一些joey老铁是吃不到桌子上的鸡的,即线程多了,cpu的资源是有限的,这样就无法提高效率了。

有时甚至可能会由于线程的增多,导致线程可能会争夺系统资源,最后导致系统崩溃!!!

🔥🔥🔥线程和进程的概念即区别

1. 进程包含线程: 即一个进程中可以包含一个或者多个线程,但是不能没有线程,当我们创建一个main函数时就是一个主线程。
2. 进程是系统资源分配的基本单位,线程是系统调度执行的基本单位。
3. 同一个进程中的所有线程其实是共用同一份系统资源的(硬盘,内存,网络带宽等)
4. 线程是当下实现并发编程的主流方式,通过多线程可以很好的利用cpu的多核心特性~但是并不是线程越多越好的,随着线程的增多(当然创建线程也会有一定的开销),可能会导致多个线程之间争夺系统资源,甚至导致系统的崩溃
5. 多个线程之间,其实也存在一定的安全问题,当一个线程产生异常时,可能也会影响其他线程的正常执行~
6. 多个进程之间一般是不会相互影响的,、一个进程崩溃,并不会影响其他进程的运行(这也称为“进程的隔离性”)。在这里插入图片描述

这个问题非常重要,是面试的高频问题,一定要牢记于心啊!!!

🔥🔥🔥如何在java中创建线程

线程其实是属于操作系统中的概念,操作系统会提供api,供程序员调用,但是不同的操作系统其实对应的api是不同的,java中jvm已经替我们将这些api封装好了,我们只需要关注jvm中的这一套api就好了。
一般我们通过使用Thread这个类来完成多线程的开发。

🗼🗼🗼继承Thread重写Run方法创建线程

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

这里的run函数的主体其实就是我们完成线程主体任务的地方,在这里实现代码的主体部分,然后通过start方法,真正在内存中创建一个线程,通过回调函数run执行相关操作。回调函数就是用户手动定义了,但是没有手动调用,但是最后被系统调用的函数。

🗼🗼🗼实现Runnable创建线程

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

这里的sleep是Thread中的库函数,可以不导入包直接调用,但是这里存在一个受查异常,所以要进行异常捕捉。由于父类中的run方法并没有这个异常所以我们只能利用try-catch,而不能把异常向上抛出。

🗼🗼🗼继承Thread利用匿名内部类实现一个线程

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

匿名内部类定义在一个类的内部,只能使用一次。

🗼🗼🗼实现Runnable利用匿名内部类创建线程

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

🗼🗼🗼利用lamda表达式创建一个线程

    public static void main(String[] args) throws InterruptedException{Thread t=new Thread(()->{while(true){System.out.println("thread hello");try{Thread.sleep(1000);}catch (InterruptedException e){throw new RuntimeException(e);}}},"自定义线程");t.start();while(true){System.out.println("hello main");Thread.sleep(1000);}}

好了,今天就分享这么多了,感兴趣的话关注不迷路哦

这篇关于【javaEE-有关CPU进程和线程实现的并发编程及二者的区别】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1057288

相关文章

Python中Tkinter GUI编程详细教程

《Python中TkinterGUI编程详细教程》Tkinter作为Python编程语言中构建GUI的一个重要组件,其教程对于任何希望将Python应用到实际编程中的开发者来说都是宝贵的资源,这篇文... 目录前言1. Tkinter 简介2. 第一个 Tkinter 程序3. 窗口和基础组件3.1 创建窗

基于C++的UDP网络通信系统设计与实现详解

《基于C++的UDP网络通信系统设计与实现详解》在网络编程领域,UDP作为一种无连接的传输层协议,以其高效、低延迟的特性在实时性要求高的应用场景中占据重要地位,下面我们就来看看如何从零开始构建一个完整... 目录前言一、UDP服务器UdpServer.hpp1.1 基本框架设计1.2 初始化函数Init详解

Java中Map的五种遍历方式实现与对比

《Java中Map的五种遍历方式实现与对比》其实Map遍历藏着多种玩法,有的优雅简洁,有的性能拉满,今天咱们盘一盘这些进阶偏基础的遍历方式,告别重复又臃肿的代码,感兴趣的小伙伴可以了解下... 目录一、先搞懂:Map遍历的核心目标二、几种遍历方式的对比1. 传统EntrySet遍历(最通用)2. Lambd

Spring Boot 中 RestTemplate 的核心用法指南

《SpringBoot中RestTemplate的核心用法指南》本文详细介绍了RestTemplate的使用,包括基础用法、进阶配置技巧、实战案例以及最佳实践建议,通过一个腾讯地图路线规划的案... 目录一、环境准备二、基础用法全解析1. GET 请求的三种姿势2. POST 请求深度实践三、进阶配置技巧1

springboot+redis实现订单过期(超时取消)功能的方法详解

《springboot+redis实现订单过期(超时取消)功能的方法详解》在SpringBoot中使用Redis实现订单过期(超时取消)功能,有多种成熟方案,本文为大家整理了几个详细方法,文中的示例代... 目录一、Redis键过期回调方案(推荐)1. 配置Redis监听器2. 监听键过期事件3. Redi

Spring Boot 处理带文件表单的方式汇总

《SpringBoot处理带文件表单的方式汇总》本文详细介绍了六种处理文件上传的方式,包括@RequestParam、@RequestPart、@ModelAttribute、@ModelAttr... 目录方式 1:@RequestParam接收文件后端代码前端代码特点方式 2:@RequestPart接

SpringBoot整合Zuul全过程

《SpringBoot整合Zuul全过程》Zuul网关是微服务架构中的重要组件,具备统一入口、鉴权校验、动态路由等功能,它通过配置文件进行灵活的路由和过滤器设置,支持Hystrix进行容错处理,还提供... 目录Zuul网关的作用Zuul网关的应用1、网关访问方式2、网关依赖注入3、网关启动器4、网关全局变

SpringBoot全局异常拦截与自定义错误页面实现过程解读

《SpringBoot全局异常拦截与自定义错误页面实现过程解读》本文介绍了SpringBoot中全局异常拦截与自定义错误页面的实现方法,包括异常的分类、SpringBoot默认异常处理机制、全局异常拦... 目录一、引言二、Spring Boot异常处理基础2.1 异常的分类2.2 Spring Boot默

基于SpringBoot实现分布式锁的三种方法

《基于SpringBoot实现分布式锁的三种方法》这篇文章主要为大家详细介绍了基于SpringBoot实现分布式锁的三种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、基于Redis原生命令实现分布式锁1. 基础版Redis分布式锁2. 可重入锁实现二、使用Redisso

SpringBoo WebFlux+MongoDB实现非阻塞API过程

《SpringBooWebFlux+MongoDB实现非阻塞API过程》本文介绍了如何使用SpringBootWebFlux和MongoDB实现非阻塞API,通过响应式编程提高系统的吞吐量和响应性能... 目录一、引言二、响应式编程基础2.1 响应式编程概念2.2 响应式编程的优势2.3 响应式编程相关技术