广告
返回顶部
首页 > 资讯 > 后端开发 > Python >浅谈java线程状态与线程安全解析
  • 493
分享到

浅谈java线程状态与线程安全解析

java线程状态java线程安全 2023-02-03 12:02:45 493人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

目录1.线程的几种状态1.1 线程的状态1.2 线程状态的转移 2.有关线程安全问题2.1 一个简单的例子2.2 造成线程不安全的原因1.线程的几种状态 1.1 线程的状态

1.线程的几种状态

1.1 线程的状态

以下就是我们线程所有的状态和意义:

NEW已经创建Thread但未创建线程
RUNNABLE可工作的. 又可以分成正在工作中和即将开始工作
BLOCKED等待(阻塞状态)
WaiTING调用wati方法(阻塞状态)
TIMED_WAITING调用sleep方法(阻塞状态)
TERMINATED系统线程执行完毕已销毁,但Thread还存在

注意:

BLOCKED 表示等待获取锁, WAITING 和 TIMED_WAITING 表示等待其他线程发来通知.

TIMED_WAITING 线程在等待唤醒,但设置了时限; WAITING 线程在无限等待唤醒 

1.2 线程状态的转移 

各线程之间的转移关系可以简化成下图:

 关于yield方法:

多线程中我们存在一个yield方法可以让线程在就绪队列中重新”排队“,不改变线程状态。相当于你去帮别人排队,但是轮到你了那个人还没回来,你就就让原本排在你后面的人换到你的位置上,但你仍然处于排队状态。这种”大公无私“的行为可以类比到我们的yield方法帮助我们理解。

public class Demo{
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() { while (true) {
                System.out.println("张三");
                // 先注释掉, 再放开
                //Thread.yield();
            }
            }
        }, "t1");
        t1.start();
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("李四");
                }
            }
        }, "t2");
        t2.start();
 
    }
}

 可以看到:

1. 不使用 yield 的时候, 张三李四大概五五开

2. 使用 yield 时, 张三的数量远远少于李四

结论: yield 不改变线程的状态, 但是会重新去排队.

2.有关线程安全问题

2.1 一个简单的例子

// 创建两个线程, 让这俩线程同时并发的对一个变量, 自增 5w 次. 最终预期能够一共自增 10w 次.
class Counter {
    // 用来保存计数的变量
    public int count;
 
    public void increase() {
        count++;
    }
}
 
public class Demo {
    // 这个实例用来进行累加.
    // public static Counter counter = new Counter();
 
    public static void main(String[] args) {
        Counter counter = new Counter();
 
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                counter.increase();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                counter.increase();
            }
        });
        t1.start();
        t2.start();
 
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("count: " + counter.count);
    }
}

大家先看到以上的代码,意思很简单,用两个线程对同一个变量进行自增操作,运行的结果如下 

看起来不太对,我们再试一次

结果有了变化,但仍然不是我们想要的结果,是什么导致了5w+5w<10w呢?其中一个原因就是线程的随机调度和改操作不具有原子性。 这些概念我们下面会详细讲,这里我们先简单了解一下。

首先我们的自增操作在cpu内其实分为三步:

1.LOAD:cpu从内存中读取数据到寄存器

2.ADD:在寄存器内实现自增

3.SAVE:将寄存器的数据写回内存中

而我们已经知道cpu对于线程调度我们可以理解为是随机的,所以会有很多种可能,比如下图

其中纵轴代表运行时间,这里我们可以看到两个线程相当于互不影响,线程1完成自增操作后又将数据写回内存由线程2再去操作,这种情况下是没有问题的。但是也可能是下面的一种情况

 此时线程1还没有将自增后的数据写回内存而线程2就已经将要修改的数据读入了寄存器,此时相当于线程2读到了那个还未自增的数据,相当于两个线程对同一个数进行了自增,所以此时相当于只自增了一次。其实情况还有很多,这里我们仅举例比较经典的例子。所以这也能够解释为什么结果大于5w而小于10w了。

2.2 造成线程不安全的原因

2.2.1 操作系统的随机调度/抢占式运行

这种是操作系统内核就已经决定的,我们无能为力。类似于我们上一个例子,就是因为线程的随机调度和操作不具有原子性造成的。 

2.2.2 操作不具有原子性 

什么是原子性

我们把一段代码想象成一个房间,每个线程就是要进入这个房间的人。如果没有任何机制保证,A进入 房间之后,还没有出来;B 是不是也可以进入房间,打断 A 在房间里的隐私。这个就是不具备原子性 的。转换成代码我们可以理解成只具有一条指令的操作。

当然这个问题我们可以通过加锁操作解决(以后会提到)。

一条 java 语句不一定是原子的,也不一定只是一条指令,比如我们上面提到的自增操作。

不保证原子性会给多线程带来什么问题

如果一个线程正在对一个变量操作,中途其他线程插入进来了,如果这个操作被打断了,结果就可能是错误的。 这点也和线程的抢占式调度密切相关. 如果线程不是 "抢占" 的, 就算没有原子性, 也问题不大.

2.2.3 多个线程修改同一个变量

1.一个线程修改变量没事

2.多个线程同时一个变量也没事

3.多个线程同时修改不同变量也没有问题

唯独需要注意多个线程修改同一个变量,如果不加以处理可能会造成我们之前讲到的例子的问题 

2.2.4 内存可见性问题 

JVM中规定了java的内存模型

线程之间的共享变量存在 主内存 (Main Memory).

每一个线程都有自己的 "工作内存" (Working Memory) .

当线程要读取一个共享变量的时候, 会先把变量从主内存拷贝到工作内存, 再从工作内存读取数据.

当线程要修改一个共享变量的时候, 也会先修改工作内存中的副本, 再同步回主内存. 

正是因为这种机制,所以可能会出现下面的问题:

由于每个线程有自己的工作内存, 这些工作内存中的内容相当于同一个共享变量的 "副本". 此时修改线程 1 的工作内存中的值, 线程2 的工作内存不一定会及时变化.通俗的讲就是 线程1针对工作内容修改了数据,而线程2此时并不一定能够及时同步修改的数据,所以可能会引发各种问题。

2.2.5 指令重排序 

所谓指令重排序是指jvm针对我们的代码,可能会在保证逻辑不变的情况下去调整指令执行的顺序以达到运行效率更高的效果。这种情况在单线程的情况下可以很好实现,而在多线程的情况下就可能会出现bug,导致程序逻辑改变。比如对于下面这行代码:

Test t=new Test();

它其实总共有三个步骤:

1.创建内存空间

2.往这个内存空间构造一个对象

3.将这个内存引用赋给t 

在单线程的情况下2,3互换并不会有上面影响,但假如在多线程情况下我们按1,3,2来执行,当执行到3时t为非null,此时线程2读取t,但是却发现是一个无效对象。 

到此这篇关于浅谈java线程状态与线程安全解析的文章就介绍到这了,更多相关java线程状态与线程安全内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 浅谈java线程状态与线程安全解析

本文链接: https://www.lsjlt.com/news/194097.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 浅谈java线程状态与线程安全解析
    目录1.线程的几种状态1.1 线程的状态1.2 线程状态的转移 2.有关线程安全问题2.1 一个简单的例子2.2 造成线程不安全的原因1.线程的几种状态 1.1 线程的状态...
    99+
    2023-02-03
    java线程状态 java线程安全
  • Java线程安全状态专题解析
    一、观察线程的所有状态 线程的状态是一个枚举类型 Thread.State public static void main(String[] args) { f...
    99+
    2022-11-13
  • Java线程安全与非线程安全解析
    ArrayList和Vector有什么区别?HashMap和HashTable有什么区别?StringBuilder和StringBuffer有什么区别?这些都是Java面试中常见的基础问题。面对这样的问题,回答是:ArrayList是非线...
    99+
    2023-05-31
    java 线程安全 ava
  • 浅谈线程的几种可用状态
    1. 新建( new ):新创建了一个线程对象。...
    99+
    2023-05-31
    线程 状态
  • Java线程安全状态的示例分析
    这篇文章主要为大家展示了“Java线程安全状态的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Java线程安全状态的示例分析”这篇文章吧。一、观察线程的所有状态线程的状态是一个枚举类型 ...
    99+
    2023-06-29
  • 浅谈Java中ArrayList线程不安全怎么办
    ArrayList线程不安全怎么办? 有三种解决方法: 使用对应的 Vector 类,这个类中的所有方法都加上了 synchronized 关键字 就和 HashMap ...
    99+
    2022-11-12
  • Java中线程状态+线程安全问题+synchronized的用法详解
    目录java中的线程状态线程安全问题案例分析多线程对同一变量进行写操作内存可见性问题指令重排序问题synchronized的用法synchronized起作用的本质修饰普通方法修饰静...
    99+
    2022-11-13
  • Java多线程之线程状态详解
    目录 线程状态停止线程线程休眠模拟网络延迟(放大问题的发生性)模拟计时线程礼让插队(线程强制执行)线程状态观测线程优先级守护线程总结 线程状态 五个状态:新生、就...
    99+
    2022-11-12
  • 浅谈JAVA 线程状态中可能存在的一些误区
    BLOCKED 和 WAITING 的区别 BLOCKED 和 WAITING 两种状态从结果上来看,都是线程暂停,不会占用 CPU 资源,不过还是有一些区别的 BLOCKED ...
    99+
    2022-11-12
  • Java解析线程的几种状态详解
    目录1. 线程的5种状态2. Java线程的6种状态3. Java线程状态的转换总结1. 线程的5种状态 从操作系统层面上,任何线程一般都具有五种状态,即创建、就绪、运行、阻塞、终止...
    99+
    2022-11-13
  • 浅谈springboot如何保证多线程安全
    目录如何保证多线程安全1.springboot在多线程并发访问下是怎么做的2.controller在多线程下如何尽可能保证线程安全,如何取舍3.小结一下单例模式与线程安全问题踩的坑下...
    99+
    2022-11-12
  • Java线程安全中的有序性浅析
    什么是有序性 在开发中,我们通常按照从上到下的顺序编写程序指令,并且希望cpu和编译器按照我们预先编写的顺序去执。但往往cpu和编译器为了提高性能、优化指令的执行顺序,会将我们编写好...
    99+
    2023-02-21
    Java线程有序性 Java线程安全 Java线程安全有序性
  • Java线程安全中的原子性浅析
    目录何为原子性解决方法CAS机制(Compare And Swap)何为原子性 原子性:一条线程在执行一系列程序指令操作时,该线程不可中断。一旦出现中断,那么就可能会导致程序执行前后...
    99+
    2023-02-21
    Java线程原子性 Java线程安全原子性
  • 详解Java的线程状态
    Java的每个线程都具有自己的状态,Thread类中成员变量threadStatus存储了线程的状态: private volatile int threadStatus = 0; ...
    99+
    2022-11-13
    Java线程状态 Java线程
  • Java线程安全与不安全实例分析
    本篇内容主要讲解“Java线程安全与不安全实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java线程安全与不安全实例分析”吧!当我们查看JDK API的时候,总会发现一些类说明写着,线程...
    99+
    2023-06-17
  • 浅谈单例模式和线程安全问题
    目录单例模式、多实例模式、和线程安全单例模式懒汉式和饿汉式对比多线程模式下的安全性多实例模式总结单例模式、多实例模式、和线程安全 单例模式 单例模式是指确保一个类仅有一个唯一的实例,...
    99+
    2023-05-14
    java单例模式 java线程安全
  • Java中线程状态+线程安全问题+synchronized的用法是什么
    这篇文章主要介绍了Java中线程状态+线程安全问题+synchronized的用法是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java中线程状态+线程安全问题+synchronized的用法是什么文章都...
    99+
    2023-06-29
  • Java多线程之线程状态的迁移详解
    目录一、六种状态二、状态迁移图三、线程状态模拟总结一、六种状态 java.lang.Thread 的状态分为以下 6 种,它们以枚举的形式,封装在了Thread类内部: NEW:表...
    99+
    2022-11-12
  • Java多线程 - 线程安全和线程同步解决线程安全问题
    文章目录 线程安全问题线程同步方式一: 同步代码块方式二: 同步方法方式三: Lock锁 线程安全问题 线程安全问题指的是: 多个线程同时操作同一个共享资源的时候可能会出现业务安全问题,称为线程安全问题。 举例:...
    99+
    2023-08-20
    java 安全 jvm
  • Java线程安全与同步实例分析
    本篇内容介绍了“Java线程安全与同步实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!线程安全问题多个线程可能会共享(访问)同一个资源...
    99+
    2023-06-30
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作