Java多线程【状态与安全问题】(上)

简介: Java多线程【状态与安全问题】

🍎一.多线程状态


🍇1.1多线程的状态形式


线程状态 说明
NEW 安排了工作, 还未开始行动
RUNNABLE 可工作的. 又可以分成正在工作中和即将开始工作
BLOCKED 这几个都表示排队等着其他事情
WAITING 这几个都表示排队等着其他事情
TIMED_WAITING 这几个都表示排队等着其他事情
TERMINATED 工作完成了


1.NEW状态:安排了工作, 还未开始行动

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

55581073f64c4980871b1b3f748450c9.png

2.RUNNABLE:可工作的. 又可以分成正在工作中和即将开始工作

public class Test {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            while (true){
                //这里面不能有人任务,这样才会显示是就绪状态
            }
        });
        thread.start();
        System.out.println(thread.getState());
    }
}


d0b7e7032d0c42aeb5bcc5abceffd4fa.png

3.TERMINATED:工作完成了


操作系统线程已经执行完毕,线程已经销毁了,但是Thread对象还存在,获取到的状态

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
                System.out.println("我滴任务完成啦!");
        });
        thread.start();
        Thread.sleep(500);
        System.out.println(thread.getState());
    }
}

82058f07a0be4508a7afefc8c88b869f.png

4.TIMED_WAITING:代码调用了sleep或者join(超时时间)

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            while (true) {
                System.out.println("我在等一会");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        Thread.sleep(500);
        System.out.println(thread.getState());
    }
}

d324b5eb794a48d6829c65c7260cab0f.png


BLOCKED,WAITING|这两个状态我们后续会将


🍇1.2状态转化分布图

b43358a940b6459f845206a2be6f5e69.png



🍎二.多线程的安全问题


🍇2.1线程安全问题的概念


如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线


程安全的。

如果多线程环境下代码运行的结果是不符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程不安全的。


🍇2.2线程不安全示例


假设我们设置一个count初始值为0,我们使用两个线程同时并且每条线程执行5万次,这样我们的预期count结果为10万,接下来我们来演示一下看看两个线程并发是否安全


public class Test {
   static class Count{
        public int count = 0;
        public void sum(){
            count++;
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Count count = new Count();
        Thread thread1 = new Thread(()->{
            for (int i = 0; i < 50000; i++) {
                count.sum();
            }
        });
        Thread thread2 = new Thread(()->{
            for (int i = 0; i < 50000; i++) {
               count.sum();
            }
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(count.count);
    }
}

0487ac6714134edfb65476856c029e90.pngd56f2f270938464b9634bebfaca0c706.png



我们发现count执行的结果并不是10万,而是5万-10万之间的随机数,这是什么原因呢?接下来我来帮助大家了解一下为什么在对对同一数据进行并发线程不安全

原子性

什么是原子性?

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

那我们应该如何解决这个问题呢?是不是只要给房间加一把锁,A 进去就把门锁上,其他人是不是就进不来了。这样就保证了这段代码的原子性了。有时也把这个现象叫做同步互斥,表示操作是互相排斥的。

一条 java 语句不一定是原子的,也不一定只是一条指令比如刚才我们看到的 n++,其实是由三步操作组成的:

1. 从内存把数据读到 CPU

2. 进行数据更新

3. 把数据写回到 CPU不保证原子性会给多线程带来什么问题如果一个线程正在对一个变量操作,中途其他线程插入进来了,如果这个操作被打断了,结果就可能是错误的。


可见性

可见性指, 一个线程对共享变量值的修改,能够及时地被其他线程看到.线程之间的共享变量存在 主内存 (Main Memory).每一个线程都有自己的 “工作内存” (Working Memory) .当线程要读取一个共享变量的时候, 会先把变量从主内存拷贝到工作内存, 再从工作内存读取数据.当线程要修改一个共享变量的时候, 也会先修改工作内存中的副本, 再同步回主内存.由于每个线程有自己的工作内存, 这些工作内存中的内容相当于同一个共享变量的 “副本”. 此时修改线程1 的工作内存中的值, 线程2 的工作内存不一定会及时变化.


cfa6b058e6294da7b53a8185db50b65c.png

情况一:


03536b43070e482b9c0684b29cb76112.png


情况二:


11080047e6554d0ca953b6ca0af7de0d.png


情况三:

f35f53b45e424a57abbfe63eb286425c.png


情况四:



6d8a192c4cd24f908e588b07b340a8b2.png


重排序问题:


6f414f748b734ee888e9216ae93dba97.png

相关文章
|
2月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
131 0
|
2月前
|
Java API 调度
从阻塞到畅通:Java虚拟线程开启并发新纪元
从阻塞到畅通:Java虚拟线程开启并发新纪元
284 83
|
3月前
|
存储 SQL 安全
Java 无锁方式实现高性能线程实战操作指南
本文深入探讨了现代高并发Java应用中单例模式的实现方式,分析了传统单例(如DCL)的局限性,并提出了多种无锁实现方案。包括基于ThreadLocal的延迟初始化、VarHandle原子操作、Record不可变对象、响应式编程(Reactor)以及CDI依赖注入等实现方式。每种方案均附有代码示例及适用场景,同时通过JMH性能测试对比各实现的优劣。最后,结合实际案例设计了一个高性能配置中心,展示了无锁单例在实际开发中的应用。总结中提出根据场景选择合适的实现方式,并遵循现代单例设计原则以优化性能和安全性。文中还提供了代码获取链接,便于读者实践与学习。
94 0
|
2月前
|
存储 Java 调度
Java虚拟线程:轻量级并发的革命性突破
Java虚拟线程:轻量级并发的革命性突破
235 83
|
4月前
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
185 0
|
3月前
|
存储 Java
说一说 JAVA 内存模型与线程
我是小假 期待与你的下一次相遇 ~
|
3月前
|
移动开发 Java
说一说 Java 是如何实现线程间通信
我是小假 期待与你的下一次相遇 ~
|
3月前
|
Java 数据挖掘 调度
Java 多线程创建零基础入门新手指南:从零开始全面学习多线程创建方法
本文从零基础角度出发,深入浅出地讲解Java多线程的创建方式。内容涵盖继承`Thread`类、实现`Runnable`接口、使用`Callable`和`Future`接口以及线程池的创建与管理等核心知识点。通过代码示例与应用场景分析,帮助读者理解每种方式的特点及适用场景,理论结合实践,轻松掌握Java多线程编程 essentials。
227 5

热门文章

最新文章