湖南汉泰建设有限公司网站/今天新闻摘抄十条

湖南汉泰建设有限公司网站,今天新闻摘抄十条,个人备案网站改企业备案,做软件开发的哪个招聘网站比较靠谱各位看官早安午安晚安呀 如果您觉得这篇文章对您有帮助的话 欢迎您一键三连,小编尽全力做到更好 欢迎您分享给更多人哦 今天我们来学习多线程编程-"掌握线程创建、管理与安全": 上一节课程我们铺垫了一系列的东西,引出来了我们的多…

各位看官早安午安晚安呀

如果您觉得这篇文章对您有帮助的话

欢迎您一键三连,小编尽全力做到更好
欢迎您分享给更多人哦

今天我们来学习多线程编程-"掌握线程创建、管理与安全"

上一节课程我们铺垫了一系列的东西,引出来了我们的多线程,接下来就让小编带领大家一起进入多线程的世界吧!!!

目录

上一节课程我们铺垫了一系列的东西,引出来了我们的多线程,接下来就让小编带领大家一起进入多线程的世界吧!!!

1.创建线程的方法:

1.1通过继承Thread类,然后重写run()方法

1.2.实现Runnnable接口,重写run方法

1.3.二者区别

2.(通过jconsole观察进程里面的多线程情况)

2.1.首先我们要知道每一个线程都是独立的执行流

2.1.jconsole

3.Thread的一些其他方法

4.中断一个线程(interrupt)

4.1.手动设置标志位

4.2.Thread内置的标志位

5.线程等待(join方法)

6.线程状态

7.线程安全问题(最重要)

8.synchronized(原子性)


1.创建线程的方法:

线程实话说是操作系统的概念程序员想要操作线程肯定就需要操作系统提供给我们一些API,但是不同的操作系统提供的API又不同 (实话说这就让小编想起了这个)

=>  java就针对上述的系统API进行了封装(跨平台嘛,(^-^)V 我们java太厉害啦)

=> 我们程序员只需要了解这一套API就够啦~~

 java提供给我们的API就是Thread类,我们创建Thread对象就可以操作系统内部的线程啦

老规矩:学习一个类,先看他的构造方法

我们大概先学这几种,还有一种线程分组的,小编现在也不了解,后续再给大家介绍吧~~

Runnable表示一个以运行的任务而已,这个任务是交给线程执行还是其他用图我们不关心

1.1通过继承Thread类,然后重写run()方法

class MyThread extends Thread{@Overridepublic void run() {System.out.println("我创建的一个新线程"); //  这个run方法里面描述了我这个线程要干啥}
}

这是我们自己创建的线程但是一个java程序跑起来,还有一个跟随进程一起创建的线程,这个线程叫做主线程,这个主线程的入口方法是main方法。

这个run方法呢是我们创建的这个线程的入口方法!!!

一个程序跑起来,从哪个方法开始执行,哪个方法就是他的入口方法。

run方法是入口方法没有错,但是这个线程我们想要跑起来,肯定要启动呀

class MyThread extends Thread{@Overridepublic void run() {System.out.println("我创建的一个新线程");}
}
public class Test {public static void main(String[] args) throws InterruptedException {Thread t = new MyThread();t.start();  // 启动线程,start方法才是真正的调用系统的API ,// 创建一个线程然后这个线程通过run方法跑起来System.out.println("这就是主线程");}

1.2.实现Runnnable接口,重写run方法

class MyRunnable implements Runnable {@Overridepublic void run() {while (true) {System.out.println("我实现Runnable,重写run方法实现的任务");}}
}public class Test {public static void main(String[] args) {Thread t = new Thread(new MyRunnable());t.start();while (true) {System.out.println("主线程");}}}

当然我们通过匿名内部类lambda表达式实现也完全没问题(上面我们继承Thread类重写run方法当然也可以使用匿名内部类的方式,但是lambda表达式不可以函数式接口定义(就是lambda表达式):一个接口有且只有一个抽象方法))

Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("通过匿名内部类实现");}});Thread t2 = new Thread(() -> {System.out.println("通过lambda表达式实现");});

1.3.二者区别

首先我们要明确创建线程的两个关键操作

1.首先要明确要执行的任务(就是想要通过这个线程干啥)

2.调用系统API创建出线程。

好,现在我们就可以明确知道他俩的区别了,耦合性不同

1.第一种方法(继承Thread的)把任务嵌套在了线程里面(后面想要修改任务,就要修改线程的源代码(大工程))

2.第二种方法:我要让线程执行其他任务,直接构造方法变成其他任务就好了。并且我这个任务又不是只给一个线程使用,其他线程想用的话直接传过去就行了。

总之:把任务分离出来,耦合性更低了,效率更高!!!

2.(通过jconsole观察进程里面的多线程情况

2.1.首先我们要知道每一个线程都是独立的执行流

就拿这个代码来说(但是这个代码打印的太快了不好观察,我们可以让他休眠一下,慢一点打印

class MyThread extends Thread{@Overridepublic void run() {System.out.println("我创建的一个新线程");}
}public class Test {public static void main(String[] args){Thread t = new MyThread();t.start(); while (true) {System.out.println("主线程");}}

休眠的方法:sleep方法是Thread类的静态方法(这个异常的处理很有讲究的,大家尽量不要犯这样的错误呀)

class MyThread extends Thread{@Overridepublic void run() {while(true){try {System.out.println("我创建的一个新线程");Thread.sleep(1000);//  sleep 这里就是一个类方法,我们直接调用就好了//  这里不能抛出异常,如果子类方法抛出额外的异常,调用者(可能只了解父类方法)可能不知道如何正确处理这些新的异常。//这里 父类的 run方法并没有抛出异常} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}public class Test {public static void main(String[] args) throws InterruptedException {Thread t = new MyThread();t.start();while (true) {Thread.sleep(1000);System.out.println("主线程");}}

可以看到两个线程的正在交替打印日志,并且不是一种规律进行打印。

你俩都是休眠1000ms,休眠后并且你你俩谁先执行都不一定(随机的),这个取决于操作系统对于调度器的的具体实现。

通过并发执行,更加充分的的利用CPU资源

2.1.jconsole

jconsole(观察多线程属性的一种方式,还有IDEA的一种一个)

jdk里面的一个可执行程序,jdk的位置:

如何启动:

1.启动之前保证idea的程序已经跑起来了

2.有的兄弟需要管理员方式运行(正常情况不行的话)

=>

3.Thread的一些其他方法

    public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("创建的线程");},"主线程");System.out.println(t.getId());  //java 给这个线程分配的idSystem.out.println(t.getName()); // 我设置的线程名字  -> 主线程System.out.println(t.isDaemon()); // 后台线程? falseSystem.out.println(t.isAlive());// 线程是否存活  false 还没开始运行t.setDaemon(true);  // 设置t线程为后台线程//这四行一下就执行完了t.start();System.out.println(t.isAlive());  //true ,不过就存活这一下就结束了,其他线程都结束了//后台线程肯定结束了// 最后发现没打印创建的线程}

4.中断一个线程(interrupt)

java里面就是让一个线程快点结束(而不是直接让一个线程中间断掉(这样一个线程会导致残留数据,不太科学))

4.1.手动设置标志位

我们用一个isQuit作为标志位,让主线程5秒后修改isQuit的值

 public static  boolean isQuit = false;  // 类属性public static void main(String[] args) throws InterruptedException {//这里就涉及到了变量的捕获的语法,但是Thread t = new Thread(() ->{while(!isQuit){//我这里捕获到的其实是拷贝外部变量的一份System.out.println("我新创建的一个线程");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("线程工作完毕");},"t线程");t.start();Thread.sleep(5000); // 五秒之后设置isQuit = trueisQuit = true;}

给大家提一个问题,毕竟当时小编在写这个代码的时候也忘了

这里不涉及是涉及匿名内部类的变量捕获吗?为什么这里不报错呢?

答:这个时候isQuit是成员变量,此时lambda访问这个成员,就不再是变量捕获的语法了,变成了“内部类访问外部类”的语法,就没有final(实际上没有被修改过的也算)的限制了

另外:对于基本数据类型,捕获的是它们的值的副本(实际上就是复制了一份)

如果是局部变量的话就会报错,因为修改了

如果一个局部变量不能解决问题就可以考虑把这个局部变量换成成员变量

但是这样写还是不太舒服

1.还要我们自己创建

2.并且如果另一个线程正在sleep就不能够及时响应

java有没有已经设置好的标志位呢?当然!!!

4.2.Thread内置的标志位

t.interrupt的作用:

    public static void main(String[] args) {Thread t = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {System.out.println("我新建的一个线程");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();   //  这里扔出一个异常,也仅仅只是打印出异常的位置信息而已,也没真正的处理什么}}System.out.println("我的循环即将终止");});t.start();try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("让线程终止");t.interrupt();// 把线程内部的标志位设置为true}

我们发现线程确实还是在运行,一张图搞懂

还有一个静态方法,那大家都用的是一个标志位实在是不太科学(但是如果是你想让两个东西抵消好像还可以)(先记住吧)

5.线程等待(join方法)

线程等待:一个线程等待另一个线程结束再执行(控制线程结束的顺序)

    public static void main(String[] args) throws InterruptedException {  //线程阻塞抛出的异常Thread t =new Thread(() ->{while(true){System.out.println("我新建的一个线程");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();System.out.println("等待线程开始");t.join(5000);  // 让主线程等待5s,也会造成线程阻塞,也要抛出异常System.out.println("join 等待结束");}

   public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {System.out.println("我新建的一个线程");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}});long t1 = System.currentTimeMillis();t.start();t.join();  // 也会造成线程阻塞,也要抛出异常long t2 = System.currentTimeMillis(); // 调度,还有创建线程的开销System.out.println(t2 - t1);  //}

6.线程状态

    public static void main(String[] args) {for(Thread.State state : Thread.State.values()){System.out.println(state);}}

//观察状态public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {try {Thread.sleep(1000);System.out.println("11111111");} catch (InterruptedException e) {throw new RuntimeException(e);}});System.out.println(t.getState()); //  NEWt.start();System.out.println(t.getState()); //RUNNABLEThread.sleep(500);System.out.println(t.getState()); //TIMED_WAITING: 由于sleep这种固定时间的方式产生的阻塞System.out.println(t.getState());  // terminated,Thread对象还在,但是我创建的对象已经跑完了}

7.线程安全问题(最重要)

简述:

单个线程下执行可以,但是多个线程执行就出现bug,这种就是线程安全问题

(写出的代码和预想的结果不同就是bug!!!)

我们先看一个代码:两个线程同时修改一个静态成员变量

   public static int count;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for(int i = 0; i < 5000; i++){count++;}});Thread t2 = new Thread(() ->{for (int i = 0; i < 5000; i++) {count++;}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);}

竟然不是10000

但是我们修改一下代码:结果就是10000了

   public static int count;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for(int i = 0; i < 5000; i++){count++;}});Thread t2 = new Thread(() ->{for (int i = 0; i < 5000; i++) {count++;}});t1.start();t1.join();t2.start();t2.join();System.out.println(count);}

到底是什么原因呢?

count++这个操作其实是分成三步进行的(CPU通过三个指令来实现的)

这种也可能(所以说这排列组合是无数种)

归根到底:

这是因为线程的抢占式运行,两个线程不是往上累加而是独立运行。

如果这个count++变成一条指令(原子的),那么线程随便执行都没问题了

所有的都变成了样子,这个时候我们就想到给这个count++进行加锁,把他变成一条指令!!!

8.synchronized(原子性)

我们就需要使用synchronized关键字给一个代码块进行加锁

   public static int count;public static void main(String[] args) throws InterruptedException {Object locker = new Object();Thread t1 = new Thread(() -> {for(int i = 0; i < 5000; i++){synchronized (locker){count++;}//synchronized,// 我们把要加锁的代码放在这个代码块里面就行了//我们如果对两个线程加相同的锁,就会造成"锁竞争/锁冲突“,毕竟我们就只有这一把锁,线程2必须等线程1解锁然后再加锁//    由于我们是对一个变量进行操作,我们还是尽量加同一把锁,两把锁,还是会并发执行//导致:那么一个线程对 count 的修改可能不会被另一个线程立即看到.}});Thread t2 = new Thread(() ->{for (int i = 0; i < 5000; i++) {synchronized (locker){count++;}}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);}

之后就变成这么执行了,变成了串行执行

t2线程由于锁的竞争(拿不到locker的监视器锁)就只能阻塞等待,一直等待到t1线程unlock之后才能获得这把锁,这样就避免了load,add,save的穿插执行。

如果我们两个线程分别给两个线程进行加锁,就还是会穿插执行。因为没有了锁竞争!!!

后面的线程安全问题后面再讲吧~~~(上一次看铠甲勇士还是在上一次~~~哈哈哈)

上述就是进程的"黑匣子":PCB如何记录着任务的一生

的全部内容了,线程的出现,我们的效率又得到了很大的提升~~~,但是管理和安全也是一个很大的问题。预知后事如何,请听下回分解~~~

能看到这里相信您一定对小编的文章有了一定的认可。

有什么问题欢迎各位大佬指出
欢迎各位大佬评论区留言修正~~

您的支持就是我最大的动力​​​!!!

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

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

相关文章

互动多媒体项目 自行车互动

该项目为UE4 +自行车骑行速度 互动项目 结果预览 : 1. 获取自行车速度 这里使用的是Arduino单片机 + 霍尔传感器 霍尔传感器: 单片机完整代码: #define HALL_PIN 2 // 霍尔传感器连接到D2(中断引脚) volatile unsigned long lastTime = 0; // …

STM32——GPIO介绍

GPIO(General-Purpose IO ports,通用输入/输出接口)模块是STM32的外设接口的核心部分,用于感知外界信号(输入模式)和控制外部设备(输出模式),支持多种工作模式和配置选项。 1、GPIO 基本结构 STM32F407 的每个 GPIO 引脚均可独立配置,主要特性包括: 9 组 GPIO 端口…

学习笔记:Python网络编程初探之基本概念(一)

一、网络目的 让你设备上的数据和其他设备上进行共享&#xff0c;使用网络能够把多方链接在一起&#xff0c;然后可以进行数据传递。 网络编程就是&#xff0c;让在不同的电脑上的软件能够进行数据传递&#xff0c;即进程之间的通信。 二、IP地址的作用 用来标记唯一一台电脑…

DeepSeek 医疗大模型微调实战讨论版(第一部分)

DeepSeek医疗大模型微调实战指南第一部分 DeepSeek 作为一款具有独特优势的大模型,在医疗领域展现出了巨大的应用潜力。它采用了先进的混合专家架构(MoE),能够根据输入数据的特性选择性激活部分专家,避免了不必要的计算,极大地提高了计算效率和模型精度 。这种架构使得 …

JetBrains学生申请

目录 JetBrains学生免费授权申请 IDEA安装与使用 第一个JAVA代码 1.利用txt文件和cmd命令运行 2.使用IDEA新建项目 JetBrains学生免费授权申请 本教程采用学生校园邮箱申请&#xff0c;所以要先去自己的学校申请校园邮箱。 进入JetBrains官网 点击立即申请&#xff0c;然…

LINUX网络基础 [五] - HTTP协议

目录 HTTP协议 预备知识 认识 URL 认识 urlencode 和 urldecode HTTP协议格式 HTTP请求协议格式 HTTP响应协议格式 HTTP的方法 HTTP的状态码 ​编辑HTTP常见Header HTTP实现代码 HttpServer.hpp HttpServer.cpp Socket.hpp log.hpp Makefile Web根目录 H…

六十天前端强化训练之第八天到第十四天——综合案例:用户管理系统

欢迎来到编程星辰海的博客讲解 看完可以给一个免费的三连吗&#xff0c;谢谢大佬&#xff01; 目录 一、知识体系详解 1. 变量与作用域 2. 箭头函数特性 3. 数组高阶函数 4. DOM操作原理 5. 事件传播机制 6. 闭包核心原理 7. 原型继承体系 8. Promise工作流程 二、综…

技术周总结 03.03 - 03.09 周日(Java监控 SpringAI)

文章目录 一、03.05 周三二、03.08 周六openAI 的Spring开发 一、03.05 周三 jvisualvm java自带的监控和故障排除工具 命令行执行后&#xff0c;会出现 JConsole 二、03.08 周六 openAI 的Spring开发 引入 spring-ai-openai-spirng-boot-starter 依赖 Spring AI http…

CI/CD—Jenkins、Maven安装

Jenkins简介 Jenkins 是一款广泛使用的开源持续集成和持续交付&#xff08;CI/CD&#xff09;工具&#xff0c;以下是对它的详细介绍&#xff1a; 基本信息 起源与发展&#xff1a;Jenkins 最早起源于 Hudson 项目&#xff0c;后来从 Hudson 项目中分离出来独立发展。自 2011 …

抽奖系统测试报告

项目链接: 管理员登录页面 项目功能: 管理员登录: 登录方式分为两种: 手机号密码登录: 正确输入密码和手机号登录 短信验证码登录: 输入手机号,等待验证码,输入验证码登录 管理员注册: 登录页面点击注册按钮即可注册管理员身份 人员管理模块: 人员管理模块分为注册…

【高级篇】大疆Pocket 3加ENC编码器实现无线RTMP转HDMI进导播台

【高级篇】大疆Pocket 3加ENC编码器实现无线RTMP转HDMI进导播台 文章目录 准备工作连接设备RTMP概念ENCSHV2推流地址设置大疆Pocket 3直播设置总结 老铁们好&#xff01; 很久没写软文了&#xff0c;今天给大家带了一个干货&#xff0c;如上图&#xff0c;大疆Pocket 3加ENC编…

【 <一> 炼丹初探:JavaWeb 的起源与基础】之 Servlet 与 JSP 的协作:MVC 模式的雏形

<前文回顾> 点击此处查看 合集 https://blog.csdn.net/foyodesigner/category_12907601.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12907601&sharereferPC&sharesourceFoyoDesigner&sharefromfrom_link <今日更新> 一、Servl…

python从入门到精通(二十六):python文件操作之Word全攻略(基于python-docx)

python文件操作之word技巧大全 word技巧基础到高级操作大全A.准备工作1. 安装python-docx库2. 导入库 B.基础操作1. 创建Word文档1.1 创建文档对象1.2 添加word标题1.3 添加word段落1.4 设置段落样式1.5 创建有序列表1.6 创建无序列表1.7添加word分页1.8 添加word图片1.9 添加w…

Debian二次开发一体化工作站:提升科研效率的智能工具

在科研领域&#xff0c;数据处理是实验成功的关键环节之一。随着实验数据的复杂性和规模不断增加&#xff0c;传统的数据处理方法已经难以满足科研人员的需求。这时&#xff0c;一体化工作站应运而生&#xff0c;成为科研实验数据处理的 “智能大脑”。 一体化工作站&#xff…

linux学习(五)(服务器审查,正常运行时间负载,身份验证日志,正在运行的服务,评估可用内存)

服务器审查 在 Linux 中审查服务器的过程包括评估服务器的性能、安全性和配置&#xff0c;以确定需要改进的领域或任何潜在问题。审查的范围可以包括检查安全增强功能、检查日志文件、审查用户帐户、分析服务器的网络配置以及检查其软件版本。 Linux 以其稳定性和安全性而闻名…

C语言_数据结构总结6:链式栈

纯c语言代码&#xff0c;不涉及C 顺序栈的实现&#xff0c;欢迎查看这篇文章&#xff1a;C语言_数据结构总结5&#xff1a;顺序栈-CSDN博客 0. 结构单元 #include<stdio.h> #include<stdlib.h> typedef int ElemType; typedef struct Linknode { ElemType…

新品速递 | 多通道可编程衰减器+矩阵系统,如何破解复杂通信测试难题?

在无线通信技术快速迭代的今天&#xff0c;多通道可编程数字射频衰减器和衰减矩阵已成为测试领域不可或缺的核心工具。它们凭借高精度、灵活配置和强大的多通道协同能力&#xff0c;为5G、物联网、卫星通信等前沿技术的研发与验证提供了关键支持。从基站性能测试到终端设备校准…

AI自动化应用的影响

生产力的迭代也终将伴随着一代人的落幕。 2025年是AI应用爆发的开局之年&#xff0c;预计3-5年现有生产关系将出现颠覆性改革。 AI自动化对经济和就业的影响是一个复杂且多维的问题&#xff0c;其长期影响取决于技术进步、政策调控、社会适应能力等多重因素的综合作用。以下从技…

润开鸿重磅首发基于“RISC-V+OpenHarmony+星闪”的“鸿锐”AI开发平台

润开鸿重磅首发基于“RISC-VOpenHarmony星闪”的“鸿锐”AI开发平台 2月28日&#xff0c;2025中国RISC-V生态大会在北京中关村国际创新中心隆重召开。作为领先的鸿蒙生态专业技术公司和终端操作系统发行版提供商&#xff0c;以及不断推进基于RISC-V与OpenHarmony全栈开源生态构…

Java 深度复制对象:从基础到实战

目录 一、深度复制的概念二、实现深度复制的方法1. 使用序列化2. 手动实现深度复制 三、总结 在 Java 编程中&#xff0c;对象的复制是一个常见的需求。然而&#xff0c;简单的复制操作&#xff08;如直接赋值&#xff09;只会复制对象的引用&#xff0c;而不是创建一个新的对象…