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

Java的并发编程1

Java的并发编程1

一、进程与线程

1、进程
  • 程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载CPU,数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理IO的。
  • 一个程序被运行,从磁盘加载这个程序的代码至内存,这时就开启了一个进程。
  • 进程就可以视为程序的一个实例。大部分程序可以同时运行多个实例进程(例如记事本、画图、浏览器等),也有的程序只能启动一个实例进程(例如网易云音乐、360安全卫士等)
2、线程
  • 一个进程之内可以分为一到多个线程。
  • 一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给CPU执行
  • Java中,线程作为最小调度单位,进程作为资源分配的最小单位。在windows中进程是不活动的,只是作为线程的容器
二者对比
  • 进程基本上是相互独立的,而线程存在于进程内,是进程的一个子集
  • 进程拥有共享的资源,如内存空间等,供其内部的线程共享
  • 进程间通信较为复杂
    • 同一台计算机的进程通信称为IPC
    • 不同计算机之间的进程通信,需要通过网络,并遵守共同的协议,例如HTTP
  • 线程通信相对简单,因为它们共享进程内的内存,一个例子是多个线程可以访问同一个共享变量
  • 线程更轻量,线程上下文切换成本一般上要比进程上下文切换低
3、并行与并发

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

一般会将这种线程轮流使用CPU的做法称为并发,concurrent

在这里插入图片描述

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

在这里插入图片描述

并发和并行的描述:

  • 并发是同一时间应对多件事情的能力
  • 并行是同一时间动手做多件事情的能力
4、异步调用

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

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

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

1)设计

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

2)结论
  • 比如在项目中,视频文件需要转换格式等操作比较费时,这时开一个新线程处理视频转换,避免阻塞主线程
  • tomcat的异步servlet也是类似的目的,让用户线程处理耗时较长的操作,避免阻塞tomcat的工作线程
  • ui程序中,开线程进行其他操作,避免阻塞ui线程

操作大文件一般都要开启新线程

多核cpu才能提升效率,单核仍然是轮流执行

  • 单核cpu下,多线程不能实际提高程序运行效率,只能为了能够在不同的任务之间切换,不同线程轮流使用cpu,不至于一个线程总占用cpu,别的线程没法干活
  • 多核cpu可以并行跑多个线程,但能否提高程序运行效率还是要分情况的
    • 有些任务,经过精心设计,将任务拆分,并行执行,当然可以提高程序的运行效率。但不是所有计算任务都能拆分
    • 也并不是所有任务都需要拆分,任务的目的如果不同,谈拆分和效率没啥意义
  • IO操作不占用cpu,只是我们一般拷贝文件使用的是【阻塞IO】,这时相当于线程虽然不用cpu,但是需要一直等待IO结束,没有充分利用线程。所以才有后面的【非阻塞IO】【异步IO】优化

二、Java线程

1、创建和运行线程
方法一:直接使用Thread

就是继承Thread类

// 创建线程对象  可以直接给构造方法设置参数指定线程的名字
Thread t = new Thread("t1") {@Override// run 方法内实现了要执行的任务public void run() {// 要执行的任务}
};
// 启动线程
t.start();
方法二:使用Runnable配合Thread

需要实现Runnable接口

把【线程】和【任务】(要执行的代码)分开

  • Thread代表线程
  • Runnable可运行的任务(线程要执行的代码)
// 创建任务对象
Runnable runnable = new Runnable() {@Overridepublic void run() {// 要执行的任务}
};
// 创建线程对象   参数1-是任务对象  参数2-线程名字
Thread t2 = new Thread(runnable, "t2");
// 启动线程
t2.start();

Java 8以后可以使用lambda精简代码:

// 创建任务对象
Runnable task2 = () -> log.debug("Hello");Thread t2 = new Thread(task2, "t2");
t2.start();
Lambda表达式

Lambda 本质上是一种 语法糖,它用来简化 函数式接口functional interface)的写法。说白了,它就是 把匿名内部类的写法变得更简洁

例子(匿名内部类写法):

Runnable r = new Runnable() {@Overridepublic void run() {System.out.println("Hello Lambda");}
};

Lambda写法:

Runnable r = () -> System.out.println("Hello Lambda");

Lambda只能用在函数式接口:

  • 函数式接口:只包含一个抽象方法的接口
  • 一般这种接口会有注解@FunctionalInterface,但是注解不是必须的,但加上可以帮助编译器检查,保证接口只有一个抽象方法。

在这里插入图片描述

Runnable接口就是函数式接口,故可以简化

ThreadRunnable的关系

  • 方法一是把线程和任务合并在一起,方法二是把线程和任务分开了
  • Runnable更容易与线程池等高级API配合
  • Runnable让任务类脱离了Thread继承体系,更灵活
方法三:FutureTask配合Thread

FutureTask能够接收Callable类型的参数,用来处理有返回结果的情况(间接实现了Runnable接口)

// 创建任务对象
FutureTask<Integer> task = new FutureTask<>(() -> {log.debug("running");return 100;
});Thread thread = new Thread(task);
thread.start();// get会阻塞线程 主线程阻塞,同步等待task执行完毕的结果
Integer result = task.get();
log.debug("结果是:{}", result);
2、线程的查看和杀死

Windows

  • 任务管理器中可以查看进程和线程数,也可以用来杀死进程
  • tasklist可以查看进程
  • taskkill杀死进程
  • PID对应的就是进程ID,故要杀死指定的进程时:taskkill /F /PID 55520/F表示强制杀死,最后输入指定的进程ID

  • 可以输入tasklist | findstr java查找含有java的进程

linux

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

可以输入ps -fe | grep java查找含有java的进程

Java

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

jconsole 是 Java 提供的一个监控和管理工具,属于 JDK 的一部分。它用于监控 Java 应用的性能、资源使用情况,以及 JVM(Java Virtual Machine)的状态。

启动使用 win + R 输入jconsole即可启动:

在这里插入图片描述

  • 如果需要远程连接(比如连接在虚拟机上启动Java进程),就按照以下的方法在虚拟机中运行自己的java程序:

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

    这里取消安全连接和认证,都填成false,连接端口都可以,ip地址就写这个虚拟机的端口即可,java类改成自己想要运行的java类(注意:如果连接不上可能是虚拟机中防火墙没关

  • 修改/etc/hosts文件将127.0.0.1映射至主机名

如果要认证访问,还需要做如下步骤:

  • 复制jmxremote.password文件
  • 修改jmxremote.passwordjmxremote.access文件的权限为600即文件所有者可 读写
  • 连接时填入controlRole(用户名),R&D(密码)
3、线程运行的原理
3.1 栈与栈帧

Java Virtual Machine StacksJava虚拟机栈)

JVM是由堆、栈、方法区所组成,其中当每个线程启动后,虚拟机就会为其分配一块栈内存。

  • 每个栈由多个栈帧Frame组成,对应着每次方法调用时所占用的内存
  • 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法
  • 每个线程的活动栈帧互不影响,因为线程都有独立的虚拟机栈
  • 共享堆上的对象才会导致线程间相互影响,这就是需要加锁/并发控制的地方
3.2 线程上下文切换(Thread Context Switch

因为以下一些原因导致cpu不再执行当前的线程,转而执行另一个线程的代码:

  • 线程的cpu时间片用完
  • 垃圾回收
  • 有更高优先级的线程需要运行
  • 线程自己调用了sleepyieldwaitjoinparksynchronizedlock等方法

Context Switch发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程池的状态,Java中对应的概念就是程序计数器(Program Counter Register),它的作用是记住下一条jvm指令的执行地址,是线程私有的

  • 状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等
  • Context Switch频繁发生会影响性能,(所以不是线程池中线程越多越好,合适的数量才行
4、常用方法
方法名static功能说明注意
start()启动一个新线程,在新的线程运行run方法中的代码start方法只是让线程进入就绪,里面代码不一定立刻运行(CPU的时间片还没分给它)。每个线程对象的start方法只能调用一次,如果调用了多次会出现IllegalThreadStateException
run()新线程启动后会调用的方法如果在构造Thread对象时传递了Runnable参数,则线程启动后会调用Rumnable中的run方法,否则默认不执行任何操
join()等待线程运行结束
join(long n)等待线程运行结束,最多等待 n 毫秒
getId()获取线程长整型的idid唯一
getName()获取线程名
setName()修改线程名
getPriority()获取线程优先级
setPriority(int)修改线程优先级
getState()获取线程状态Java中线程状态是用6个enum表示,分别为:NEWRUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED
isInterrupted()判断是否被打断不会清除打断标记
isAlive()线程是否存活(还没有运行完毕)
interrupt()打断线程如果被打断线程正在sleepwaitjoin会导致被打断的线程抛出InterruptedException,并清除打断标记;如果打断的正在运行的线程,则会设置打断标记;park的线程被打断,也会设置打断标记
interrupted()static判断当前线程是否被打断会清除打断标记
currentThread()static获取当前正在执行的线程
sleep(long n)static让当前执行的线程休眠n毫秒,休眠时让出cpu 的时间片给其它线程
yield()static提示线程调度器让出当前线程对CPU的使用主要是为了测试和调试

文章转载自:

http://SvrQQwRs.qczyz.cn
http://NmXbkerB.qczyz.cn
http://v7R0mXGw.qczyz.cn
http://MbYGj6ky.qczyz.cn
http://Fs2RSz30.qczyz.cn
http://fZ21dGLz.qczyz.cn
http://7pcGiAvX.qczyz.cn
http://dz72Uiy8.qczyz.cn
http://HkQ0rGce.qczyz.cn
http://L1dsqlfY.qczyz.cn
http://Ps06FaMW.qczyz.cn
http://u4mLdkLg.qczyz.cn
http://KtqxXW4v.qczyz.cn
http://AQuLUIIo.qczyz.cn
http://EgpfLo5X.qczyz.cn
http://rAOQNMXB.qczyz.cn
http://S4J09pxV.qczyz.cn
http://JagbthN1.qczyz.cn
http://hyDVyTaB.qczyz.cn
http://ms4FJ28u.qczyz.cn
http://e04ytDu0.qczyz.cn
http://PFhp6wKp.qczyz.cn
http://4aVL9kFj.qczyz.cn
http://ccFpFSis.qczyz.cn
http://Oo6MAoOp.qczyz.cn
http://KUFJAkel.qczyz.cn
http://0LZ9L5Ux.qczyz.cn
http://OQQEHE3o.qczyz.cn
http://w6BeGBgO.qczyz.cn
http://kUgUXGnw.qczyz.cn
http://www.dtcms.com/a/384636.html

相关文章:

  • 使用prometheus operator监控部署在k8s集群外的mysql实例
  • Notepad++ 8.7 64位安装教程(附安装包)​
  • 《大数据之路1》笔记3:数据管理
  • 【代码随想录day 27】 力扣 376. 摆动序列
  • 使用conda导出虚拟环境
  • LeetCode热题100--105. 从前序与中序遍历序列构造二叉树--中等
  • 计算机网络---数据链路层上
  • 《FastAPI零基础入门与进阶实战》第18篇:Token验证改善--CRUD中应用
  • QT(4)
  • DevOps历程--Drone安装使用详细教程
  • 微信小程序选择图片、视频、音频
  • 【C++上岸】C++常见面试题目--网络篇(第二十三期)
  • mapbox进阶,使用jsts实现平角缓冲区
  • A股大盘数据-20250915分析
  • MySQL服务启动全平台指南:从Windows服务、Linux systemctl到macOS的完整攻略
  • 八、vue3后台项目系列——封装layout页面下切换组件Appmain
  • 学习React-12-useEffect
  • MFC_Button
  • [K8S学习笔记]YAML相关
  • 贪心算法在物联网能耗优化中的应用
  • 使用paddlepaddle-Gpu库时的一个小bug!
  • 从 Linux 到 Kubernetes:操作系统的演变与云原生未来
  • Java网络编程:(socket API编程:TCP协议的 socket API -- 服务器端处理请求的三个步骤)
  • 新能源汽车总装车间案例:四台S7-1200通过无线网桥同步控制16组ET 200SP的秘诀
  • k8s事件驱动运维利器 shell operator
  • GitHub Actions 部署配置
  • java后端工程师进修ing(研一版‖day45)
  • k8s核心资料基本操作
  • Redis 在电商系统中的应用:高并发场景下的架构艺术
  • RK3588:MIPI底层驱动学习——芯外拾遗第一篇:从四个模块到整个“江湖”