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

下期再见嗷;

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

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

相关文章

VB.net实战(VSTO):解决WPS Ribbon图标灰色背景

问题&#xff1a;用VSTO制作插件&#xff0c;在MS Office中图标显示正常&#xff0c;但在WPS Office中图标显示为灰色背景 原因&#xff1a;使用的图标是纯透明背景的&#xff0c;这样的图标在WPS中会变为灰色背景。 以下这个解决办法是我自己摸索出来的&#xff0c;对您有用的…

在.NET用C#将Word文档转换为HTML格式

将Word文档转换为HTML格式尤其具有显著的优势&#xff0c;它不仅能够确保文档内容在多种设备和平台上保持一致灵活的显示&#xff0c;还便于通过网络进行传播和集成到各种Web应用中。随着越来越多的企业和开发者寻求更灵活、更具兼容性的文件处理方式&#xff0c;.NET框架下的C…

在服务器上增加新网段IP的路由配置

在服务器上增加新网段IP的路由配置 前提条件步骤一:检查当前路由表步骤二:添加新路由步骤三:验证新路由步骤四:持久化路由配置脚本示例结论在网络管理中,路由配置是一项基本且重要的任务。它决定了数据包在网络中的传输路径。本文将详细介绍如何在服务器上增加新的路由配置…

鸿蒙学习构建视图的基本语法(二)

一、层叠布局 // 图片 本地图片和在线图片 Image(https://developer.huawei.com/allianceCmsResource/resource/HUAWEI_Developer_VUE/images/080662.png) Entry Component//自适应伸缩 设置layoutWeight属性的子元素与兄弟元素 会按照权重进行分配主轴的空间// Position s…

电脑风扇声音大怎么办? 原因及解决方法

电脑风扇是电脑的重要组件之一&#xff0c;它的作用是为电脑的各个部件提供冷却&#xff0c;防止电脑过热。然而&#xff0c;有时候我们会发现电脑风扇的声音特别大&#xff0c;不仅影响我们的使用体验&#xff0c;也可能是电脑出现了一些问题。那么&#xff0c;电脑风扇声音大…

OpenVela——专为AIoT领域打造的开源操作系统

目录 一、系统背景与开源 1.1. 起源 1.2. 开源 二、系统特点 2.1. 轻量化 2.2. 标准兼容性 2.3. 安全性 2.4. 高度可扩展性 三、技术支持与功能 3.1. 架构支持 3.2. 异构计算支持 3.3. 全面的连接套件 3.4. 开发者工具 四、应用场景与优势 4.1. 应用场景 4.2. …

хорошо哈拉少wordpress俄语主题

хорошо哈拉少wordpress俄语主题 wordpress俄文网站模板&#xff0c;推荐做俄罗斯市场的外贸公司建俄语独立站使用。 演示 https://www.jianzhanpress.com/?p7360

TOSUN同星TsMaster使用入门——3、使用系统变量及c小程序结合panel面板发送报文

本篇内容将介绍TsMaster中常用的Panel面板控件以及使用Panel控件通过系统变量以及c小程序来修改信号的值&#xff0c;控制报文的发送等。 目录 一、常用的Panel控件介绍 1.1系统——启动停止按钮 1.2 显示控件——文本框 1.3 显示控件——分组框 1.4 读写控件——按钮 1.…

程序设计:排版、检验报告的上下标解决几种办法

【啰嗦两句】 本文重点在于提供几个针对排版文档、各种检验报告系统等程序设计时&#xff0c;遇到的上下标录入、绘制展示等问题的应对办法&#xff0c;但是准确地说&#xff0c;并没有非常优秀的方案。 【上下标难题】 一般的行业或许对上下标并没有严格要求&#xff0c;多数…

绘图专用,26个常见流程图符号及其解释

关注作者 当您设计网站、构建应用程序或绘制业务系统时&#xff0c;您需要一种方法来清晰地绘制步骤和用户流程。虽然您可以使用流程图来概述这些过程&#xff0c;但箭头和方框只能帮助您到目前为止。为了清楚地表达您的意思&#xff0c;您需要流程图符号。 为了帮助解释每个流…

【STM32-学习笔记-11-】RTC实时时钟

文章目录 RTC实时时钟一、RTC简介二、RTC框图三、RTC基本结构四、RTC操作注意事项五、RTC函数六、配置RTCMyRTC.c 七、示例&#xff1a;实时时钟①、main.c②、MyRTC.c③、MyRTC.h RTC实时时钟 一、RTC简介 RTC&#xff08;Real Time Clock&#xff09;实时时钟 RTC是一个独立…

Asp .Net Core 实现微服务:集成 Ocelot+Nacos+Swagger+Cors实现网关、服务注册、服务发现

什么是 Ocelot ? Ocelot是一个开源的ASP.NET Core微服务网关&#xff0c;它提供了API网关所需的所有功能&#xff0c;如路由、认证、限流、监控等。 Ocelot是一个简单、灵活且功能强大的API网关&#xff0c;它可以与现有的服务集成&#xff0c;并帮助您保护、监控和扩展您的…

性价比1.2V电压基准替代

前言&#xff1a; 小于2V的电压基准比较少&#xff0c;且价格稍贵&#xff0c;对于要求不高的场合&#xff0c;1117可以替代使用&#xff0c;温度系数低于 100ppm/C, 价格便宜。 1117是线性稳压器的一种&#xff0c;一般情况下&#xff0c;输出电压可调。 如下述的1117&#xf…

使用 Java 和 FreeMarker 实现自动生成供货清单,动态生成 Word 文档,简化文档处理流程。

在上一篇博客中主要是使用SpringBootApache POI实现了BOM物料清单Excel表格导出&#xff0c;详见以下博客&#xff1a; Spring Boot Apache POI 实现 Exc&#xff08;&#xff09;el 导出&#xff1a;BOM物料清单生成器&#xff08;支持中文文件名、样式美化、数据合并&#…

探索与创作:2024年CSDN平台上的成长与突破

文章目录 我与CSDN的初次邂逅初学阶段的阅读CSDN&#xff1a;编程新手的避风港初学者的福音&#xff1a;细致入微的知识讲解考试复习神器&#xff1a;技术总结的“救命指南”曾经的自己&#xff1a;为何迟迟不迈出写博客的第一步兴趣萌芽&#xff1a;从“读”到“想写”的初体验…

SSM课设-学生管理系统

【课设者】SSM课设-学生管理系统 技术栈: 后端: SpringSpringMVCMybatisMySQLJSP 前端: HtmlCssJavaScriptEasyUIAjax 功能: 学生端: 登陆 学生信息管理 个人信息管理 老师端: 多了教师信息管理 管理员端: 多了班级信息管理 多了年级信息管理 多了系统用户管理

力扣 打家劫舍

动态规划&#xff0c;当前状态由前两个状态获得&#xff0c;滚动数组。 题目 从题可以看出要达到最高金额时&#xff0c;要从相邻的房屋拿。因此是当前房屋的金额隔一个做累加&#xff0c;当然还需要跟前一个相邻的房屋做比较&#xff0c;便于取到哪边金额更高&#xff0c;因此…

【Django开发】django美多商城项目完整开发4.0第12篇:商品部分,表结构【附代码文档】

本教程的知识点为&#xff1a; 项目准备 项目准备 配置 1. 修改settings/dev.py 文件中的路径信息 2. INSTALLED_APPS 3. 数据库 用户部分 图片 1. 后端接口设计&#xff1a; 视图原型 2. 具体视图实现 用户部分 使用Celery完成发送 判断帐号是否存在 1. 判断用户名是否存在 后…

Redis的安装和使用--Windows系统

Redis下载地址&#xff1a; windows版本readis下载&#xff08;GitHub&#xff09;&#xff1a; https://github.com/tporadowski/redis/releases &#xff08;推荐使用&#xff09; https://github.com/MicrosoftArchive/redis/releases 官网下载&#xff08;无Windows版本…

Linux操作命令之云计算基础命令

一、图形化界面/文本模式 ctrlaltF2-6 图形切换到文本 ctrlalt 鼠标跳出虚拟机 ctrlaltF1 文本切换到图形 shift ctrl "" 扩大 ctrl "-" 缩小 shift ctrl "n" 新终端 shift ctrl "t" 新标签 alt 1,…