多线程Thread(初阶三:线程的状态及线程安全)

目录

一、线程的状态

二、线程安全


一、线程的状态

1.NEW Thread:对象创建好了,但是还没有调用 start 方法在系统中创建线程。

2.TERMINATED: Thread 对象仍然存在,但是系统内部的线程已经执行完毕了。

3.RUNNABLE: 就绪状态,表示这个线程正在 cpu 上执行,或者准备就绪随时可以去 pu 上执行。

4.TIMED WAITING: 指定时间的阻塞.就在到达一定时间之后自动解除阻塞使用 sleep 会进入这个状态 使用带有超时时间的join也会。

5.WAITING: 不带时间的阻塞(死等),必须要满足一定的条件,才会解除阻塞join 或者 wait 都会进入 WAITING。

6.BLOCKED: 由于锁竞争,引起的阻塞.(后面线程安全的时候具体介绍)

这六种状态在生命周期的大概位置,如图:

用代码形式测试 NEW 、RUNNABLE、TERMINATED 状态

代码:

public class ThreadDemo3 {
    private static int count = 0;
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                count++;
            }
            System.out.println("count: " + count);
        });
        //t线程开始前
        System.out.println("t线程开始前: " + t.getState());

        t.start();
        //t线程执行中
        System.out.println("t线程执行中: " + t.getState());

        //t线程结束后
        t.join();
        System.out.println("t线程结束后: " + t.getState());
    }
}

执行效果:

学习这些状态,最大的作用就是调试多线程代码bug的时候,给我们提供重要的参考依据当某个程序卡住了,也就是意味着一些关键的线程阻塞了,我们就可以观察线程的状态,分享出一些原因。

注意:一个线程只能start一次,也就是说只有是NEW状态的线程才能start。

二、线程安全

线程安全:某个代码,不管它是单个线程执行,还是多个线程执行,都不会产生bug,这个情况就成为“线程安全”。

线程不安全:某个代码,它单个线程执行,不会产生bug,但是多个线程执行,就会产生bug,这个情况就成为 “线程不安全”,或者 “存在线程安全问题”。

举个线程不安全例子,现在,我们想计算一个变量的自增次数,它循环了100000次,用两个线程去计算,各自计算循环50000次的次数。

代码:

public class ThreadDemo4 {
    private static int count = 0;
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 1; i < 50000; i++) {
                count++;
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 50000; i <= 100000; i++) {
                count++;
            }
        });
        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("count: " + count);
    }
}

我们知道,从1到10_0000,肯定是自增了10_0000次,但是我们看看输出结果如何?

答案却不是10_0000,是50000多次,这是为何呢?原因就是多线程代码,它们是并发执行的。

这个count++是由cpu的三个指令构成的:

(1)load 从内存中读取数据到cpu的寄存器中。

(2)add 把寄存器中的值 + 1。

(3)save 把寄存器中的值写回到内存中。

因为上面两个线程是并发执行的,那么 t1 和 t2 线程的执行顺序就是无序的,他们可能同时读取数据,自增完再+1,下面用图示他们的一些可能性。

暂时就画这么多,因为线程并发的执行结果个数是无数的,并不是简单的排列组合就能穷举出来,因为并发的原因,可能 t1 线程它执行了两次,才执行一次 t2 线程,也可能更多,或者 t2 执行的次数更多,t1 线程只执行一次。就又要排列组合了,这些情况都是有可能的。

这时,t1 和 t2自增的时候,就可能拿的是同一个值,这两线程的其中一个自增后,没放回内存中,另一个线程就又拿了,这肯定是不符合我们预期的。

所以我们从上面的可能情况找,符合我们预期的效果就只有这两个了,如图

那么这种情况也就是串行化执行,执行完 t1,再执行t2,或者两个顺序相反。

代码:

public class ThreadDemo4 {
    private static int count = 0;
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 1; i < 50000; i++) {
                count++;
            }
        });
        Thread t2 = new Thread(() -> {
            try {
                t1.join();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            for (int i = 50000; i <= 100000; i++) {
                count++;
            }
        });
        t1.start();

        t2.start();
        t2.join();

        System.out.println("count: " + count);
    }
}

执行效果:

但这样就没必要使用多线程了,因为串行化,一个线程就能搞定了,使用多线程也没太大意义。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Python 中创建 Sentinel-2 RGB 合成图像

一、前言 下面的python代码将带您了解如何从原始 Sentinel-2 图像创建 RGB 合成图像的过程。 免费注册后&#xff0c;可以从 Open Access Hub 下载原始图像。 请注意&#xff0c;激活您的帐户可能需要 24 小时&#xff01; 二、准备工作 &#xff08;1&#xff09;导入必要的库…

【Mybatis-Plus篇】Mybatis-Plus基本使用

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

跳转应用市场详情页market

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、商业变现、人工智能等&#xff0c;希望大家多多支持。 未经允许不得转载 目录 一、导读二、概览三、跳转到各大厂商应…

思科模拟器操作命令

模式 思科模拟器常见的模式有 用户模式 能够操作的命令比较少 特权模式特权模式下面可以操作的比较多 全局模式 接口模式 用户模式进入特权模式: 命令enable 特权模式进行全局模式命令: configure terminal 退出命令 exit命令&#xff1a;返回上一层&#xff0c;即一步一步…

Windows核心编程 进程间通信

目录 进程间通信概述 发送消息 WM_COPYDATA DLL共享段 文件映射 文件相关API CreateFile ReadFile WriteFile CloseHandle SetFilePointerEx 设置文件指针 获取文件大小 GetFileSize 结构体 LARGE_INTEGER 文件映射用于读写文件数据 文件映射用于进程间通信(带文…

百度搜索框中的下拉提示关键词提取

效果图 代码有点多&#xff0c;绑定资源了 导出excel如下 贴心养眼背景图鼠标点击小爱心

pat实现基于邻接矩阵表示的深度优先遍历

void DFS(Graph G, int v) {visited[v] 1;printf("%c ", G.vexs[v]);for (int i 0; i < G.vexnum; i) {if (!visited[i] && G.arcs[v][i]) DFS(G, i);} }

C# 读写FDX-B(ISO11784/85)动物标签源码

本示例使用的发卡器&#xff1a;EM4305 EM4469 ISO11784/85协议125K低频FXD-B动物标签读写发卡器-淘宝网 (taobao.com) using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using S…

API 设计:使用 Node.js 和 Express.js 的综合教程

API&#xff08;应用程序编程接口&#xff09;设计涉及创建一个高效而强大的接口&#xff0c;允许不同的软件应用程序相互交互。 说明 本教程将指导您使用 Node.js 和 Express.js 作为核心技术来规划、设计和构建 API。但是&#xff0c;这些原则可以应用于任何语言或框架。我们…

APP软件外包开发需要注意的问题

在进行APP软件开发时&#xff0c;有一些关键问题需要特别注意&#xff0c;以确保项目的成功和用户满意度。以下是一些需要注意的问题&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 清晰的需求定义&a…

详解STUN与TR111

STUN协议定义了三类测试过程来检测NAT类型&#xff1a; Test1&#xff1a;STUN Client通过端口{IP-C1:Port-C1}向STUN Server{IP-S1:Port-S1}发送一个Binding Request&#xff08;没有设置任何属性&#xff09;。STUN Server收到该请求后&#xff0c;通过端口{IP-S1:Port-S1}把…

统计二叉树中的伪回文路径 : 用位运用来加速??

题目描述 这是 LeetCode 上的 「1457. 二叉树中的伪回文路径」 &#xff0c;难度为 「中等」。 Tag : 「DFS」、「位运算」 给你一棵二叉树&#xff0c;每个节点的值为 1 到 9 。 我们称二叉树中的一条路径是 「伪回文」的&#xff0c;当它满足&#xff1a;路径经过的所有节点值…

一个正整数转为2进制和8进制,1的个数相同的第23个数是什么?

package cn.com;import java.lang.*;//默认加载public class C2 {//10进制转8进制static int HtoO(int n){int cnt 0;while(n!0){cntn%8;n/8;}return cnt;}//10进制转2进制static int HtoB(int n){int cnt 0;while(n!0){cntn%2;n/2;}return cnt;}public static void main(Str…

Windows服务设置多个服务依赖项避免服务启动失败找不到数据库

添加多个服务依赖项建议通过命令行的方式添加&#xff1a; winr键打开命令行 cmd 命令行添加命令如下&#xff1a; sc config "thinvent-auth" depend "MySQL57"/"RabbitMQ"/"Redis" sc config "服务A" depend "服务…

C#,《小白学程序》第十四课:随机数(Random)第一,几种随机数的计算方法与代码

1 文本格式 /// <summary> /// 《小白学程序》第十四课&#xff1a;随机数&#xff08;Random&#xff09;第一&#xff0c;几种随机数的计算方法与代码 /// 本课初步接触一下随机数。 /// </summary> /// <param name"sender"></param> ///…

LiveVIS视图库1400-如何切换数据库?默认使用的数据库是什么?如何切换到Mysql/MariaDB?

LiveVIS视图库1400-如何切换数据库&#xff1f;默认使用的数据库是什么&#xff1f;如何切换到Mysql/MariaDB? 1、切换成Mysql/Mariadb数据库1.1 连接数据库1.2 创建数据库实例1.3 配置.ini文件1.4 重启完成切换 1、切换成Mysql/Mariadb数据库 LiveVIS 默认使用 sqlite3 文件…

重新开启GPT Plus充值通道——基于前端开发者工具

chatGPT PLUS充值通道的关闭 由于chatGPT用户激增&#xff0c;近日&#xff0c;OpenAI的CEO Sam Altman宣布需要暂停新用户对ChatGPT Plus的订阅。在X上&#xff0c;他表达了对于确保用户体验的承诺&#xff0c;同时也提到了用户可以通过应用程序内的通知功能来了解服务恢复的…

笔记本电脑可以投屏到电视吗?Win、Mac、Linux分别怎么投屏?

如果你的电视是安卓电视&#xff0c;那么答案是&#xff1a;完全可以&#xff01; 不管你的笔记本电脑是Windows系统、macOS系统还是Linux系统&#xff0c;你都可以借助AirDroid Cast的电脑客户端或网页版&#xff0c;将电脑屏幕投屏到安卓智能电视上。 首先&#xff0c;你需要…

Unity技美35——再URP管线环境下,配置post后期效果插件(post processing)

前两年在我的unity文章第10篇写过&#xff0c;后效滤镜的使用&#xff0c;那时候大部分项目用的还是unity的基础管线&#xff0c;stander管线。 但是现在随着unity的发展&#xff0c;大部分项目都用了URO管线&#xff0c;甚至很多PC端用的都是高效果的HDRP管线&#xff0c;这就…

网络数据结构skb_buff原理

skb_buff基本原理 内核中sk_buff结构体在各层协议之间传输不是用拷贝sk_buff结构体&#xff0c;而是通过增加协议头和移动指针来操作的。如果是从L4传输到L2&#xff0c;则是通过往sk_buff结构体中增加该层协议头来操作&#xff1b;如果是从L4到L2&#xff0c;则是通过移动sk_…