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

javaEE初阶————多线程初阶(2)

今天给大家带来第二期啦,保证给大家讲懂嗷;

1,线程状态

NEW安排了工作还未开始行动
RUNNABLE可工作的,或者即将工作,正在工作
BLOCKED排队等待
WAITING排队等待其他事
TIMED_WAITING排队等待其他事
TERMINATED工作完成了

1.1 观察线程的所有状态 

 1) new 

new就是创建了Thread对象还没有开始使用,也就是没有start

public class Demo1 {public static void main(String[] args) {Thread t1 = new Thread(()->{System.out.println(1111); });System.out.println(t1.getState());}
}

看运行结果;

 

2) RUNNABLE

可工作的,又被分为即将开始工作和正在工作,这个就是在操作系统层面进程的就绪状态,用代码说明吧,简单明了;

Thread t2 = new Thread(()->{while(true){System.out.println(11111);}});t2.start();

运行结果一定是疯了一样的打印11111,我们借助jconsole来看线程状态;

正在运行RUNNABLE;

3)TERMINATED 

工作完成了,虽然内核中的线程已经结束了,但是Thread对象还在;

4) WAITING

死等,还是上代码,

public class Demo1 {public static void main(String[] args) throws InterruptedException {Thread t2 = new Thread(()->{while(true){System.out.println(11111);}});t2.start();t2.join();System.out.println(22222);}
}

 我们让主线程main来等着t2看看的状态;

运行结果还是无线的,看不到22222;

mian的状态是WAITING,正在死等谈线程结束,这里面我们没有命名,系统自动给t2起名字了,t2还是RUNNABLE状态;BLOCKED状态我们之后说,我们还没有讲锁;

5) TIMED_WAITING

不是死等了,有时间限制的等待,(你还在等你的女神吗,她只会影响你变强的速度!!);

public class Demo2 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{while (!Thread.interrupted()){System.out.println("等一等~");try {Thread.sleep(1000);} catch (InterruptedException e) {break;}}});t1.start();t1.join(10000);t1.interrupt();System.out.println(Thread.currentThread().getName() + "劳资不等了!");}
}

看这个代码,我们让mian线程等待t1线程10秒,但是t1线程是没有停下来的意思的,运行结果

 

我们在运行一次,趁着这10秒来看看main线程的状态,

 TIMED_WAITING 有时间的等待;

 sleep也是会陷入有时间的等待的;我们刚才是站在线程之间的角度,而他t1线程中

t1线程在sleep之前是RUNNABLE, 在sleep时候是TIMED_WAITNG,之后还是RUNNABLE;

我们来试试;

public class Demo3 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{while (!Thread.interrupted()){System.out.println("等一等~");try {System.out.println(Thread.currentThread().getState());Thread.sleep(5000);System.out.println(Thread.currentThread().getState());} catch (InterruptedException e) {break;}}});t1.start();}
}

sleep前后都是RUNNABLE状态,而休眠的时候是TIMED_WAITING状态; 

1.2 线程状态和状态转移的意义

多线程编程中,为啥要引入这些状态呢,我们在1多线程程序中了解多线程状态是我们调试代码成功的关键,这也是我们学习多线程最基础的部分;

———————————————————————————————————————————

2,多线程带来的风险——(线程安全)

2.1 线程安全的概念

什么是线程安全?就我们单线程代码在多线程程序中能够按照预期正常运行我们就说该多线程程序是线程安全的;

2.2 观察线程不安全

我们来写一个多线程代码,让两个线程对同一操作数进行修改;

public class Demo1 {public static int count = 0;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);}
}

代码中我们让两个线程对成员变量count进行修改, 启动两个线程,并且让主线程等待两个线程,我们来看运行结果;

8千多,吞我1000多的数字,怎么个事,这个有两个原因,一个是原子性,另一个是罪魁祸首,就是线程调度的随机性,这两个线程是并发执行的;我们一会儿来详细讲一下这两种情况的解决办法;

2.3 线程不安全的原因

先来讨论下join;

join:

我们在代码中使用了join,同学们想过为啥要使用join没有,既然我们说过线程调度是随机的,那么有没有可能打印的count是0呢,屏蔽join后运行

是0吧,这就是因为操作系统的随机调度,线程1,2还在给count++,而我们的main线程只有打印,自然就程序开始就打印,不等那两个线程啦,我们加上join就让main等等那两个线程,这时又有同学问了,你刚才打印的8000多的数字,是不是因为count还没++完呢,你main线程就先打印了呢?   这一点事不可能1的,我们再来讲解一下

这也对,那也对,结果就是不对,为啥啊,线程并发执行并且随机调度!奶奶的我刚才了解了,那就因为这个给我++操作吃了?它咋吃的啊?

这就要我们站在CPU的角度来看了,

 就这一小段指令在CPU上是三段指令

1,读取 load 把内存中的值读到CPU上的寄存器中

2,修改 add 将寄存器中的值++

3,存储 save 将寄存器中的值放回内存中

我们所说的线程的随机调度是可能发生在这几步CPU操作当中的,可能我们线程二一顿++,到线程一又变成一了;这个我们马上详解来画;

我们先来聊聊原子可见性:

原子性:

我们不讲概念,我们就想想购票,如果你正在买最后一张票,你购买成功,在没有及时更新数据库的时候另一个人也看到了这张票,把它买下,那么一张票卖给了两个人,这不就是错误吗,我们想想为什么会出现这个原因,因为购票的操作不是原子的,购票操作有查票,买票,把数据更新到数据库中,我们在最后更新数据库的时候让其他线程插队了,这不就是刚才两个线程给count++的情况吗,我们要做的是把狗票操作锁到一块,让其他线程不能插队!

原子不可再分,这便是原子性;
可见性:

一个线程对共享变量的修改,在其他线程能够看到

JMM————java内存模型 

java虚拟机规范中定义了java内存模型————原因就是我们的特点——一次运行到处编译

为了屏蔽操作系统和各种硬件的内存访问差异;

每个线程独有的 “工作内存”  (WorkingMemory)————可以理解为寄存器

线程之间的共享变量:“主内存”  (MainMemory)————可以理解为内存

总之呢,导致内存不安全的原因呢:

1,根本) 线程之间随机调度,抢占式执行

2,多个线程同时修改一个变量

3,修改操作不是原子的

4,内存可见性

5,指令重排序(这个我们后面说)

———————————————————————————————————————————

2.4 解决之前的线程不安全问题

两个方法,

1,

public class Demo2 {public static int count = 0;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);}
}

直接修改程序执行的顺序,直接不启动t2线程,先让t1线程执行完,在去执行t2,但是并发很慢,我们想让他俩一起执行呀;

第二种方法:锁

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

我们接下来就好好讲讲锁; 

———————————————————————————————————————————

3,synchronized 关键字

3.1 synchronized的特性

1)互斥

两个线程要同时使用一个锁对象时会发生阻塞等待:

Object locker = new Object();synchronized(locker){System.out.println(11);}

互斥是发生在两个synchronized语句使用同一个锁对象的时候,锁对象就是locker,我们可以定义为任何类型,这个对象也可以有自己的用途,代码块中的System.out.println就是我们加锁的语句,进代码块加锁,出代码块解锁;我们解释下刚才那段代码Deom3那个;

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

我们这里是对count++这一操作加锁,让他在CPU上的三步指令操作可以当做一个了,我们必须执行完三步指令才能让其他线程执行,否则一直阻塞等待; 

2)可重入

同一个线程多次对使用一个锁不会发生阻塞等待;

public class Demo3 {static int count = 0;public static void main(String[] args) throws InterruptedException {Object locker = new Object();Thread t1 = new Thread(()->{synchronized (locker){synchronized (locker){count++;}}});t1.start();t1.join();System.out.println(count);}
}

我们看这段代码,我们在t1线程使用了两个锁,都去竞争locker,

我们实际来运行一下, 

“你刚才Dog叫什么呢,这不没问题吗”,“冤枉啊”,这是因为java中的锁是可重入锁,内部使用计数器来实现;可重入锁呢,他是在让锁对象内部保存了是哪个线程持有了这把锁,后序在对当前锁对象加锁的时候检查一下是那个线程,

3.2 synchronized 使用式例

1)修饰代码块,指定锁对象

public class Demo4 {public static void main(String[] args) {Object locker = new Object();Thread t1 = new Thread(()->{synchronized (Thread.currentThread()){System.out.println(1111);}});Thread t2 = new Thread(()->{synchronized (locker){System.out.println(2222);}});t1.start();t2.start();}
}

我们可以使用任意锁对象或者线程对象本身;

 

2)synchronized修饰普通方法

 Object locker = new Object();public synchronized int add(int a,int b){return a+b;}//等价于public int add2(int a,int b){synchronized (locker){return a+b;}}

3)synchronized修饰静态方法

public synchronized static int add3(int a,int b){return a+b;}

不一一介绍了; 

3.3 Java标准库中的线程安全类

我们之前学的数据结构,在多线程中基本都是线程不安全的,

这里给大家列举一些安全的,但先不一一展开讲了;

1,vector

2,HashTable

3,ConcurrentHashMap(强推)

4,StringBuffer

下期再见嗷;

相关文章:

javaEE初阶————多线程初阶(2)

今天给大家带来第二期啦&#xff0c;保证给大家讲懂嗷&#xff1b; 1&#xff0c;线程状态 NEW安排了工作还未开始行动RUNNABLE可工作的&#xff0c;或者即将工作&#xff0c;正在工作BLOCKED排队等待WAITING排队等待其他事TIMED_WAITING排队等待其他事TERMINATED工作完成了 …...

Redis学习笔记1【数据类型和常用命令】

Redis学习笔记 基础语法 1.数据类型 String: 最基本的类型&#xff0c;可以存储任何数据&#xff0c;例如文本或数字。示例值为 hello world。Hash: 用于存储键值对&#xff0c;适合存储对象或结构体。示例值为 {"name": "Jack", "age": 21}。…...

JavaWeb项目——查询角色列表到页面中——转发模式

一、知识点 1、req.getRequestDispatch与resp.sendRedirect跳转方式的比较 一、实现原理 1、req.getRequestDispatcher&#xff1a; 属于服务器端跳转&#xff0c;在服务器内部将请求转发给另一个资源&#xff08;如另一个 Servlet 或 JSP 页面&#xff09;。调用 getReques…...

feign调用跳过HTTPS的SSL证书校验配置详解

一、问题抛出 如果不配置跳过SSL证书校验&#xff0c;当Feign客户端尝试连接到一个使用自签名证书的服务器时&#xff0c;可能会抛出类似以下的异常&#xff1a; javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building faile…...

今天也是记录小程序进展的一天(破晓时8)

嗨嗨嗨朋友们&#xff0c;今天又来记录一下小程序的进展啦&#xff01;真是太激动了&#xff0c;项目又迈出了重要的一步&#xff0c;231啦&#xff01;感觉每一步的努力都在积累&#xff0c;功能逐渐完善&#xff0c;离最终上线的目标越来越近了。大家一直支持着这个项目&…...

SQL-leetcode—1084. 销售分析 III

1084. 销售分析 III 表&#xff1a; Product --------------------- | Column Name | Type | --------------------- | product_id | int | | product_name | varchar | | unit_price | int | --------------------- product_id 是该表的主键&#xff08;具有唯一值的列&…...

Linux C\C++编程-文件位置指针与读写文件数据块

【图书推荐】《Linux C与C一线开发实践&#xff08;第2版&#xff09;》_linux c与c一线开发实践pdf-CSDN博客 《Linux C与C一线开发实践&#xff08;第2版&#xff09;&#xff08;Linux技术丛书&#xff09;》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 Linu…...

Flask简介与安装以及实现一个糕点店的简单流程

目录 1. Flask简介 1.1 Flask的核心特点 1.2 Flask的基本结构 1.3 Flask的常见用法 1.3.1 创建Flask应用 1.3.2 路由和视图函数 1.3.3 动态URL参数 1.3.4 使用模板 1.4 Flask的优点 1.5 总结 2. Flask 环境创建 2.1 创建虚拟环境 2.2 激活虚拟环境 1.3 安装Flask…...

【自动化测试】—— Appium使用保姆教程

目录 一. 连接手机 1. 授权 2. 调试 3. 获取参数 二. 启动APP 1. 启动Appium服务 2. 启动Appium Inspector 3. 配置Appium Inspector 三. 功能说明 1. 主菜单功能 2. 快照视图菜单 3. 元素视图菜单 四. 常见问题 1. appPackage有多个设备时 一. 连接手机 1. 授权 首先将手机的开…...

西门子【Library of General Functions (LGF) for SIMATIC S7-1200 / S7-1500】

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 通用函数库 (LGF) 扩展了 TIA Portal 中用于 PLC 编程的 STEP 7 指令&#xff08;数学函数、时间、计数器 等&#xff09;。该库可以不受限制地使用&#xff0c;并包含 FIFO 、搜索功能、矩阵计算、 astro 计…...

IntelliJ IDEA 2023.3 中配置 Spring Boot 项目的热加载

IntelliJ IDEA 2023.3 中配置 Spring Boot 项目的热加载 在 IntelliJ IDEA 2023.3 中配置 Spring Boot 项目的热加载&#xff0c;可以让你在不重启应用的情况下看到代码修改的效果。以下是详细的配置步骤&#xff1a; 添加 spring-boot-devtools 依赖 在 pom.xml 文件中添加 …...

Python----Python高级(正则表达式:语法规则,re库)

一、正则表达式 1.1、概念 正则表达式&#xff0c;又称规则表达式,&#xff08;Regular Expression&#xff0c;在代码中常简写为regex、 regexp或RE&#xff09;&#xff0c;是一种文本模式&#xff0c;包括普通字符&#xff08;例如&#xff0c;a 到 z 之间的字母&#xff0…...

通过Ukey或者OTP动态口令实现windows安全登录

通过 安当SLA&#xff08;System Login Agent&#xff09;实现Windows安全登录认证&#xff0c;是一种基于双因素认证&#xff08;2FA&#xff09;的解决方案&#xff0c;旨在提升 Windows 系统的登录安全性。以下是详细的实现方法和步骤&#xff1a; 1. 安当SLA的核心功能 安…...

Node.js接收文件分片数据并进行合并处理

前言&#xff1a;上一篇文章讲了如何进行文件的分片&#xff1a;Vue3使用多线程处理文件分片任务&#xff0c;那么本篇文章主要看一下后端怎么接收前端上传来的分片并进行合并处理。 目录&#xff1a; 一、文件结构二、主要依赖1. express2. multer3. fs (文件系统模块)4. pat…...

Lsky-Pro在线图片搭建教程(Docker部署方式)

Lsky Pro+ 是一个使用 PHP 语言,采用 Laravel 框架开发的一款 Web 图片管理程序,中文名:兰空图床。如果你需要一个在线图床程序,那么这个开源项目可以帮助到你,部署流程非常简单。本章教程记录如何部署Lsky-Pro。 一、拉取镜像 docker pull halcyonazure/lsky-pro-docke…...

“深入浅出”系列之算法篇:(2)openCV、openMV、openGL

OpenCV是一个的跨平台计算机视觉库,可以运行在Linux囚、Windows 和Mac OS操作系统上。它轻量级而且高效,由一系列 C函数和少量C类构成&#xff0c;同时也提供了Python 接口&#xff0c;实现了图像处理和计算机视觉方面的很多通用算法。 OpenMV是一个开源&#xff0c;低成本&am…...

AI 新动态:技术突破与应用拓展

目录 一.大语言模型的持续进化 二.AI 在医疗领域的深度应用 疾病诊断 药物研发 三.AI 与自动驾驶的新进展 四.AI 助力环境保护 应对气候变化 能源管理 后记 在当下科技迅猛发展的时代&#xff0c;人工智能&#xff08;AI&#xff09;无疑是最具影响力的领域之一。AI 技…...

从CRUD到高级功能:EF Core在.NET Core中全面应用(三)

目录 IQueryable使用 原生SQL使用 实体状态跟踪 全局查询筛选器 并发控制使用 IQueryable使用 在EFCore中IQueryable是一个接口用于表示可查询的集合&#xff0c;它继承自IEnumerable但具有一些关键的区别&#xff0c;使得它在处理数据库查询时非常有用&#xff0c;普通集…...

【记录】Jenkins版本及JDK关系介绍的官网地址

Redhat Jenkins Packages...

vue3-json-viewer和vue-json-pretty插件使用,vue3 json数据美化展示

本文介绍vue3如何进行json数据pretty展示 1 vue3-json-viewer 1.1 安装 npm install vue3-json-viewer --save1.2 全局引入 在main.ts中引入&#xff0c;然后直接在组件中使用 import { createApp } from vue import App from ./App.vue import JsonViewer from "vue3…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器

——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的​​一体化测试平台​​&#xff0c;覆盖应用全生命周期测试需求&#xff0c;主要提供五大核心能力&#xff1a; ​​测试类型​​​​检测目标​​​​关键指标​​功能体验基…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...