Java并发编程总结(一)

news2024/11/18 7:48:27

一、进程与线程

1、进程与线程

1.1、进程

程序一般认为是静态存储的,而进程则是活动的,真正执行的、动态加载到内存的,被CPU执行的。

  • 程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至CPU,数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理IO的
  • 当一个程序被运行,从磁盘加载这个程序的代码至内存,这时就开启了一个进程。
  • 程序是静态的,可以把进程看成是一个实例。
  • 进程就可以视为程序的一个实例。大部分程序可以同时运行多个实例进程(例如记事本、画图、浏览器等),也有的程序只能启动一个实例进程(例如网易云音乐、360安全卫士等)

1.2、线程

  • 一个进程之内可以分为一到多个线程
  • ·一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给 CPU执行·
  • Java 中,线程作为最小调度单位(调度单位:CPU根据线程去找对应的指令),进程作为资源分配的最小单位(进程是管资源,加载指令,管理内存,管理IO)。在 windows 中进程是不活动的,只是作为线程的容器 【线程是负责执行指令,进程是管理指令】

1.3、两者比较

  • 进程基本上相互独立的,而线程存在于进程内,是进程的一个子集进程拥有共享的资源,如内存空间(管理和分配内存空间)等,供其内部的线程共享
  • 进程间通信较为复杂:
    • 同一台计算机的进程通信称为IPC (Inter-process communication)·
    • 不同计算机之间的进程通信,需要通过网络,并遵守共同的协议,例如 HTTP
  • 线程通信相对简单,因为它们共享进程内的内存,一个例子是多个线程可以访问同一个共享变量
  • 线程更轻量,线程上下文切换成本一般上要比进程上下文切换低

上下文切换:举个简单例子,当电脑内存不够时,要执行一个任务,可以把暂时不用的代码临时存储起来,然后切换到其他进程执行。


2、并行和并发

2.1、并发

当要让CPU同一时间执行多个线程,需要依赖操作系统中的任务调度器,可以把CPU的时间(时间片)分给不同的线程使用(也就是让多个线程轮流的去使用CPU)。让CPU轮流去执行多个线程(当一个线程的指令执行一半时,下次又让CPU去执行,可以在上一次执行的结束位置开始继续去往下执行指令),这样同一时间内应对多件事情的能力,称为 “并发”的能力。

单核 cpu 下,线程实际还是 串行执行 的。操作系统中有一个组件叫做任务调度器,将 cpu 的时间片(windows 下时间片最小约为 15 毫秒)分给不同的线程使用,只是由于 cpu 在线程间(时间片很短)的切换非常快,人类感觉是 同时运行的

总结为一句话就是:微观串行,宏观并行般会将这种 线程轮流使用 CPU 的做法称为并发,concurrent


2.2、并行 

多核 cpu下,每个 核(core) 都可以调度运行线程,这时候线程可以是并行的。

即使是多核CPU,也不可能是完全并行执行线程,但分配给一个线程的时间片用完,还是会分配给其他线程,因此,这种情况下是并发+并行。

  • 并发 (concurrent) 是同一时间应对 (dealing with) 多件事情的能力
  • 并行 (parallel) 是同一时间动手做 (doing) 多件事情的能力

3、异步、同步调用 

从方法调用的角度来讲,如果

  • ·需要等待结果返回,才能继续运行就是同步
  • 不需要等待结果返回,就能继续运行就是异步

注意:同步在多线程中还有另外一层意思,是让多个线程步调一致

1)多线程可以让方法执行变为异步的(即不要巴巴干等着)比如说读取磁盘文件时,假设读取操作花费了5秒钟,如果没有线程调度机制,这5秒调用者什么都做不了,其代码都得暂停...

2)比如在项目中,视频文件需要转换格式等操作比较费时,这时开一个新线程处理视频转换,避免阻塞主线程

tomcat 的异步 servlet 也是类似的目的,让用户线程处理耗时较长的操作,避免阻塞 tomcat 的工作线程ui程序中,开线程进行其他操作,避免阻塞ui 线程


二、java线程 

1、创建和运行线程

每个java程序在运行的时候,都会 创建一个线程,该线程为主线程

①方法一:直接使用Thread

需要注意的是,在定义好java中的Thread对象,在还没启动前,并没有和CPU中的线程相关联。需要调用Thread中的start启动后,才真正的把该线程交给操作系统中的任务调度器,并将其时间片分配给CPU去执行。

Thread t1 = new Thread("t1"){
    //线程要执行的任务(重写的run方法)
    @Override
    public void run() {
        log.debug("执行任务1!");
    }
};
t1.start();
log.debug("执行任务2!");

②方法二:使用Runnable配合Thread

把线程的任务分离出来

Runnable r = new Runnable() {
    @Override
    public void run() {
        log.debug("执行任务1");
    }
};
// Runnable r1 = ()-> log.debug("执行任务1");

//直接把runnable的lambda写法返回结果传到Thread构造方法中
Thread t1 = new Thread(()->{
    //线程要执行的任务(重写的run方法)
    //...
    log.debug("执行任务2!");
});
Thread t = new Thread(r);
t.start();
t1.start();


③方法三:FutureTask能够接收Callable 类型的参数,用来处理有返回结果的情况 

//创建FutureTask任务对象,此处注意FutureTask内部构造方法需要创建一个Callable实例,重写call方法返回结果
FutureTask<Integer>futureTask = new FutureTask<>(new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        log.debug("执行任务");
        Thread.sleep(2000);
        return 200;
    }
});
Thread t = new Thread(futureTask,"t1");
t.start();
//注意要返回task的结果,需要调用FutureTask的get方法,由于任务对象中调用Thread.sleep(2000),因此需要等待线程2s后才会返回200给主线程main
int result = futureTask.get();
log.debug("result="+result);


2、查看进程线程的方法 

Windows

  • tasklist 查看进程
  • taskkill 杀死进程

Linux

  • ps -fe 查看所有进程
  • ps -fT -p 查看某个进程
  • kill 杀死进程
  • top 按大写H切换是否显示进程
  • top -H -p 查看某个进程(PID)

Java

  • jps命令查看所有java进程
  • jstack查看某个Java进程(PID)的所有线程状态
  • jconsole来查看某个Java进程中线程的运行情况(图形界面)

jconsole远程监控配置:

【需要以如下方式运行你的java类】

java -Djava.rmi.server.hostname='ip地址-Dcom.sun.management.imxremote-Dcom.sun,management.jmxremote,port=~连接端口’-Dcom,sun,management.jmxremote.ssl=是否安全连接 -Dcom.sun.management.imxremote.authenticate=是否认证 java类

3、线程的原理 

3.1、栈与栈帧

Java Virtual Machine Stacks (Java 虚拟机栈)

我们都知道JVM 中由堆、栈、方法区所组成,其中栈内存是给谁用的呢?其实就是线程,每个线程启动后,虚拟机就会为其分配一块栈内存

每个栈由多个栈帧(Frame)组成,栈帧对应着栈中每次方法调用时所占用的内存。每个线程只能有一个活动栈,对应着当前正在执行的那个方法

 

 


3.2、栈帧图解

(1)类加载 (注意:方法区在本地内存) 

(2)给main线程分配栈内存,并将main线程交给任务调度器调度执行,之后CPU就把时间片分配给主线程

程序计数器是栈私有的,记录当前该执行哪行代码了,CPU根据程序计数器的记录来知道要从哪个代码开始执行(可以理解为一个存档点)

 

返回地址对应程序的退出地址 


3.3、多线程

示例演示: 

@Slf4j
public class Test {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            while(true){
                fun1(10);
            }
        });
        t.start();
        fun1(20);
    }
    public static void fun1(Integer temp){
        log.debug("fun1:"+temp);
        fun2(temp);
    }
    public static void fun2(Integer temp){
        log.debug("fun2:"+temp);
    }
}

分别给主线程中的fun1(20)和t线程中的fun1(10)打断点,观察栈帧的变化

【注意】断点得选择Thread,选All就看不了两个线程分别运行,只能看到同时运行的结果 

 除了通过这里IDEA观察,也可以使用命令行jconsole观察

 


3.4、线程上下文切换(Thread Context Switch)

因为以下一些原因导致 cpu 不再执行当前的线程,转而执行另一个线程的代码(可以简单理解为,一个线程的使用,到不使用的过程,即为 “线程上下文切换”

  • 线程的cpu时间片用完
  • 垃圾回收(会暂停当前所有的工作线程,让垃圾回收的线程去回收垃圾,这时工作线程就会全部暂停,暂停就会导致上下文切换)
  • 有更高优先级的线程需要运行

线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法当Context Switch 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java 中对应的概念就是程序计数器(Program Counter Register),它的作用是记住下一条jvm指令的执行地址,是线程私有的

  • 状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等
  • Context Switch频繁发生会影响性能(当CPU核心数少于线程数时,CPU就会频繁地在这些线程中轮流切换,那么线程上下文切换的成本也就越高,因此会影响性能)

4、常用方法

方法

功能说明

注意

start()

启动一个新线程在新的线程运行run方法中的代码

start 方法只是让线程进入就绪,里面代码不一定立刻运行(CPU的时间片还没分给它)。每个线程对象的start方法只能调用一次,如果调用了多次会出现IllegalThreadStateException

run()

新线程启动后会调用的方法

如果在构造Thread对象时传递了Runnable 参数,则线程启动后会调用Runnable中的run 方法,否则默认不执行任何操作。但可以创建Thread的子类对象,来覆盖默认行为

join()

等待线程运行结束

join(long n)

等待线程运行结束最多等待 n毫秒

getId()

获取线程长整型的id 唯一id

getName)

获取线程名

setName(String)

修改线程名

getPriority()

获取线程优先级

setPriority(int)

修改线程优先级

java中规定线程优先级是1~10的整数,较大的优先级能提高该线程被CPU调度的机率

getState()

获取线程状态

Java中线程状态是用6个enum 表示,分别为:NEW、RUNNABLE(就绪)、BLOCKED、RUNNING(运行)、WAITING、TIMED WAITING(阻塞)

isInterrupted ()

判断线程是否被打断

不会清除打断标记

interrupt()

打断线程

如果被打断线程正在sleep,wait,join会导致被打断的线程抛出InterruptedException,并清除打断标记;如果打断的正在运行的线程,则会设置打断标记;park 的线程被打断,也会设置打断标记

interrupted() static

判断当前线程是否被打断

会清除打断标记

isAlive()

线程是否存活(还没有运行完毕)

currentThread() static

获取当前正在执行的线程

sleep(long n) static

让当前执行的线程休眠n毫秒,休眠时让出cpu 的时间片给其它线程

yield() static

提示线程调度器让出当前线程对CPU的使用

主要是为了测试和调试

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

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

相关文章

解决 JavaScript heap out of memory 报错

前台运行项目时候提示内存溢出 解决 先执行&#xff1a; export NODE_OPTIONS"--max-old-space-size4096" 再运行&#xff1b; nom run serve

算法---双指针练习-4(盛水最多的容器)

题目 1. 题目解析2. 讲解算法原理3. 编写代码 1. 题目解析 题目地址&#xff1a;盛水最多的容器 2. 讲解算法原理 算法的主要思路是使用双指针的方法&#xff0c;通过不断调整指针的位置来计算面积&#xff0c;并更新最大面积。具体步骤如下&#xff1a; 初始化左指针x为数组…

Chrome浏览器好用的几个扩展程序

Chrome好用的扩展程序 背景目的介绍JsonHandle例子未完待续。。。。。。 背景 偶然在往上看到Chrome有很多好用的扩展程序&#xff0c;比较好用&#xff0c;因此记录下比较实用的扩展程序。 目的 记录Chrome浏览器好用的插件。 介绍 JsonHandle下载以及无法扩展插件的解决…

C#实现线性查找算法

C#实现线性查找算法 以下是使用 C# 实现线性查找算法的示例代码&#xff1a; using System;class Program {static int LinearSearch(int[] array, int target){for (int i 0; i < array.Length; i){if (array[i] target){return i; // 如果找到目标&#xff0c;返回其索…

《时间贫困》

作者&#xff1a;【英】凯茜霍姆斯 深陷困境&#xff1a;时间贫困且精疲力竭 我们生活在生产力至上的文化中&#xff0c;忙碌已经成了一种身份的象征&#xff0c;也是个人价值的一种体现。然而&#xff0c;基于我个人的经历和研究&#xff0c;我发现这种忙碌的生活状态并不能…

【Web开发】深度学习HTML(超详细,一篇就够了)

&#x1f493; 博客主页&#xff1a;从零开始的-CodeNinja之路 ⏩ 收录文章&#xff1a;【Web开发】深度学习html(超详细,一篇就够了) &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 HTML1. HTML基础1.1 什么是HTML1.2 认识HTML标签1.3 HTML文件基本…

计算机网络(五)

网络层 网络层的主要目的是实现网络互连&#xff0c;进而实现数据包在各网络之间的传输。 要实现网络层&#xff0c;主要解决三个问题&#xff1a; ①网络层向运输层提供怎样的服务&#xff1f;&#xff08;“可靠传输“、”不可靠传输“&#xff09; ②网络层寻址 ③路由选择…

GCN 翻译 - 3

3 SEMI-SUPERVISED NODE CLASSIFICATION 这里简单引入一个例子&#xff0c;利用图上信息传播的方式的一个灵活的模型&#xff0c;我们来解决一个图上节点分类的半监督问题。正如在introduction里面提到的&#xff0c;我们应用数据X和图结构的邻接矩阵锁提出的模型f(X,A)在图结…

python 网络库集锦

目录 通用网络库 网络爬虫框架 1.功能齐全的爬虫 2.其他 HTML/XML解析器 1.通用 2.清理 文本处理 自然语言处理 浏览器自动化与仿真 多重处理 异步网络编程库 队列 云计算 网页内容提取 WebSocket DNS解析 计算机视觉 通用网络库 1.urllib -网络库(stdlib)。…

关于GPU显卡的介绍

一.关于英伟达历代产品架构 显卡是一种计算机硬件设备,也被称为显示适配器或图形处理器。目前的硬件部分主要由主板、芯片、存储器、散热器&#xff08;散热片、风扇&#xff09;等部分。显卡的主要芯片是显卡的主要处理单元。显卡上也有和计算机存储器相似的存储器&#xff0…

Nginx配置文件的整体结构

一、Nginx配置文件的整体结构 从图中可以看出主要包含以下几大部分内容&#xff1a; 1. 全局块 该部分配置主要影响Nginx全局&#xff0c;通常包括下面几个部分&#xff1a; 配置运行Nginx服务器用户&#xff08;组&#xff09; worker process数 Nginx进程PID存放路径 错误…

【深入理解设计模式】模板方法模式

模板方法模式 模板方法模式是一种行为设计模式,它定义了一个操作中的算法骨架,将某些步骤延迟到子类中实现。模板方法模式使得子类可以不改变算法结构的情况下,重新定义算法的某些特定步骤。 概述 在面向对象程序设计过程中&#xff0c;程序员常常会遇到这种情况&#xff1a;…

力扣同类题:重排链表

很明显做过一次 class Solution { public:void reorderList(ListNode* head) {if(!head||!head->next)return;ListNode *fasthead,*lowhead;ListNode *prenullptr,*curnullptr,*nextnullptr;while(fast->next!nullptr){fastfast->next;if(fast->next)fastfast->…

线程-创建线程的方法、线程池

1.创建线程一共有哪几种方法&#xff1f; 继承Thread类创建线程 继承Thread类&#xff0c;重写run()方法&#xff0c;在main()函数中调用子类的strat()方法 实现Runnable接口创建线程 先创建实现Runnable接口的类&#xff0c;重写run()方法&#xff0c;创建类的实例对象&#…

【Python】科研代码学习:七 TrainingArguments,Trainer

【Python】科研代码学习&#xff1a;七 TrainingArguments&#xff0c;Trainer TrainingArguments重要的方法 Trainer重要的方法使用 Trainer 的简单例子 TrainingArguments HF官网API&#xff1a;Training 众所周知&#xff0c;推理是一个大头&#xff0c;训练是另一个大头 之…

Linux 理解进程

目录 一、基本概念 二、描述进程-PCB 1、task_struct-PCB的一种 2、task_ struct内容分类 三、组织进程 四、查看进程 1、ps指令 2、top命令 3、/proc文件系统 4、在/proc文件中查看指定进程 5、进程的工作目录 五、通过系统调用获取进程标示符 1、getpid()/get…

空间复杂度(数据结构)

概念&#xff1a; 空间复杂度也是一个数学表达式&#xff0c;是对一个算法在运行过程中临时占用存储空间大小的量度 。 空间复杂度不是程序占用了多少bytes的空间&#xff0c;因为这个也没太大意义&#xff0c;所以空间复杂度算的是变量的个数。空间复杂度计算规则基本跟实践复…

nicegui学习使用

https://www.douyin.com/shipin/7283814177230178363 python轻量级高自由度web框架 - NiceGUI (6) - 知乎 python做界面&#xff0c;为什么我会强烈推荐nicegui 秒杀官方实现&#xff0c;python界面库&#xff0c;去掉90%事件代码的nicegui python web GUI框架-NiceGUI 教程…

EI级 | Matlab实现PCA-GCN主成分降维结合图卷积神经网络的数据多特征分类预测

EI级 | Matlab实现PCA-GCN主成分降维结合图卷积神经网络的数据多特征分类预测 目录 EI级 | Matlab实现PCA-GCN主成分降维结合图卷积神经网络的数据多特征分类预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现PCA-GCN主成分降维结合图卷积神经网络的数据多…

用conda创建虚拟环境

下载好conda之后&#xff0c;在跑代码之前&#xff0c;可以用conda来创建虚拟环境&#xff0c;然后在虚拟环境中下载包pip之类的。 创建步骤如下&#xff1a; 1.conda create --name hhh 其中hhh为我的虚拟环境的名字&#xff0c;之后选择y即yes即可继续创建 可以看到&#…