Java线程安全状态专题解析

 更新时间:2022年03月02日 14:27:18   作者:/少司命  
线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况

一、观察线程的所有状态

线程的状态是一个枚举类型 Thread.State

 public static void main(String[] args) {
        for (Thread.State state : Thread.State.values()){
            System.out.println(state);
        }
    }

NEW: 安排了工作, 还未开始行动

RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作.就绪状态

BLOCKED: 这几个都表示排队等着其他事情

WAITING: 这几个都表示排队等着其他事情

TIMED_WAITING: 这几个都表示排队等着其他事情

TERMINATED: 工作完成了.

二、线程状态和状态转移的意义

NEW:Thread对象有了,但是PCB还没有

RUNNABLE:线程正在CPU上执行或者即将到CPU上执行(PCB在就绪队列中,随时可能被调度到)

WAITING:wait方法导致

TIMED_WAITING:sleep方法导致

BLOCKED:等待锁导致

TERMINATED:对象还在,但PCB已经没了

public static void main(String[] args) {
        Thread t = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 100_00; i++){
 
                }
            }
        };
        System.out.println("线程启动前:" + t.getState());
 
        t.start();
        while (t.isAlive()){
            System.out.println("线程运行中:" + t.getState());
        }
        System.out.println("线程结束后:" + t.getState());
    }

三、多线程带来的风险

线程不安全的原因

①线程是抢占式执行的

线程不安全的万恶之源,线程之间的调度完全由内核负责,用户代码中感知不到,也无法控制。线程之间谁先执行,谁后执行,谁执行到哪里从CPU上下来,这样的过程用户无法控制也无法感知到的。

②自增操作不是原子的

每次++都能拆成三个步骤

        把内存中的数据读取到CPU中

        把CPU中的数据+1

        把计算的数据写回内存中

如果两个线程串行执行,此时计算结果为2。

如果两个线程并行执行,线程1进行++操作到一半的时候,线程也进行了++操作,此时自增两次,但结果为1。

必须保证线程1save结束了,线程2再load,此时计算结果才正确

③多个线程尝试修改同一个变量

如果是一个线程修改一个变量,线程安全

如果多个线程读取同一个变量,线程安全

如果多个线程修改不同的变量。线程安全

④内存可见性导致线程安全问题

⑤指令重排序

Java的编译器在编译代码时,会对指令进行优化,调整指令的先后顺序,保证原有的逻辑不变的情况下,提高程序的运行效率

四,解决线程安全问题

锁-synchronized

未加锁

static class Counter{
        public int count = 0;
 
         public void increase(){
            count++;
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Thread t1 = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++){
                    counter.increase();
                }
            }
        };
        Thread t2 = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++){
                    counter.increase();
                }
            }
        };
        t1.start();
        t2.start();
        t1.join();
        t2.join();
 
        System.out.println(counter.count);
    }

 已加锁

static class Counter{
        public int count = 0;
 
        synchronized public void increase(){
            count++;
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Thread t1 = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++){
                    counter.increase();
                }
            }
        };
        Thread t2 = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++){
                    counter.increase();
                }
            }
        };
        t1.start();
        t2.start();
        t1.join();
        t2.join();
 
        System.out.println(counter.count);
    }

此处的synchronized就是针对counter这个对象来加锁,进入increase方法内部,就把加锁状态设为ture,increase方法退出之后,就把加锁状态设为false,如果某个线程已经把加锁状态设为ture,此处的其他的线程尝试去加锁,就会阻塞

synchronized的特性——刷新内存

synchronized 的工作过程:

        1. 获得互斥锁

        2. 从主内存拷贝变量的最新副本到工作的内存

        3. 执行代码

        4. 将更改后的共享变量的值刷新到主内存

        5. 释放互斥锁

synchronized的特性——互斥

 public static void main(String[] args) {
        Object locker = new Object();
 
        Thread t1 = new Thread(){
            @Override
            public void run() {
                Scanner scanner = new Scanner(System.in);
                synchronized (locker) {
                    System.out.println("输入一个整数");
                    int num = scanner.nextInt();
                    System.out.println("num= " + num);
                }
            }
        };
        t1.start();
 
        Thread t2 = new Thread(){
            @Override
            public void run() {
                while (true){
                    synchronized (locker){
                        System.out.println("线程2获取到锁");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        };
        t2.start();
    }

一旦线程一获取到锁,并且没有释放的话,线程2就会一直在锁这里阻塞等待

 public static void main(String[] args) {
        Object locker1 = new Object();
        Object locker2 = new Object();
 
        Thread t1 = new Thread(){
            @Override
            public void run() {
                Scanner scanner = new Scanner(System.in);
                synchronized (locker1) {
                    System.out.println("输入一个整数");
                    int num = scanner.nextInt();
                    System.out.println("num= " + num);
                }
            }
        };
        t1.start();
 
        Thread t2 = new Thread(){
            @Override
            public void run() {
                while (true){
                    synchronized (locker2){
                        System.out.println("线程2获取到锁");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        };
        t2.start();
    }

 不是同一把锁,就不回出现竞争,就没有互斥了。

public static void main(String[] args) {
        Object locker1 = new Object();
        Object locker2 = new Object();
 
        Thread t1 = new Thread(){
            @Override
            public void run() {
                Scanner scanner = new Scanner(System.in);
                synchronized (locker1.getClass()) {
                    System.out.println("输入一个整数");
                    int num = scanner.nextInt();
                    System.out.println("num= " + num);
                }
            }
        };
        t1.start();
 
        Thread t2 = new Thread(){
            @Override
            public void run() {
                while (true){
                    synchronized (locker2.getClass()){
                        System.out.println("线程2获取到锁");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        };
        t2.start();
    }

这个代码中,两个线程都在针对locker1和locker2的类对象进行竞争,此处的locker1和locker2的类型都是Object,对应的对象都是相同的对象。 

到此这篇关于Java线程安全状态专题解析的文章就介绍到这了,更多相关Java 线程安全内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 值得分享的超全文件工具类FileUtil

    值得分享的超全文件工具类FileUtil

    这篇文章主要为大家详细介绍了超全的文件工具类FileUtil,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08
  • java线程池ThreadPoolExecutor实现原理详解

    java线程池ThreadPoolExecutor实现原理详解

    这篇文章主要介绍了java线程池ThreadPoolExecutor实现原理详解,ThreadPoolExecutor是线程池实现类,会动态创建多个线程,并发执行提交的多个任务,需要的朋友可以参考下
    2023-12-12
  • springboot集成mybatis-plus全过程

    springboot集成mybatis-plus全过程

    本文详细介绍了如何在SpringBoot环境下集成MyBatis-Plus,包括配置maven依赖、application.yaml文件、创建数据库和Java实体类、Mapper层、Service层和Controller层的设置,同时,还涵盖了时间自动填充、分页查询、多对一和一对多的数据库映射关系设置
    2024-09-09
  • MyBatis高级映射和查询缓存

    MyBatis高级映射和查询缓存

    这篇文章主要介绍了MyBatis高级映射和查询缓存的相关资料,需要的朋友可以参考下
    2016-06-06
  • Java中HttpServletRequestWrapper的使用与原理详解

    Java中HttpServletRequestWrapper的使用与原理详解

    这篇文章主要介绍了Java中HttpServletRequestWrapper的使用与原理详解,HttpServletRequestWrapper 实现了 HttpServletRequest 接口,可以让开发人员很方便的改造发送给 Servlet 的请求,需要的朋友可以参考下
    2024-01-01
  • Java8新特性之Optional使用详解

    Java8新特性之Optional使用详解

    这篇文章主要介绍了Java8新特性之Optional使用详解,为了解决空指针异常更加优雅,Java8 提供了 Optional 类库,Optional 实际上是个容器,它可以保存类型T的值,或者仅仅保存null,,需要的朋友可以参考下
    2023-08-08
  • java中实现汉字按照拼音排序(示例代码)

    java中实现汉字按照拼音排序(示例代码)

    这篇文章主要是对java中将汉字按照拼音排序的实现代码进行了详细的分析介绍。需要的朋友可以过来参考下,希望对大家有所帮助
    2013-12-12
  • java实现九宫格拼图游戏

    java实现九宫格拼图游戏

    这篇文章主要为大家详细介绍了java实现九宫格拼图游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-07-07
  • SpringBoot项目jar发布后如何获取jar包所在目录路径

    SpringBoot项目jar发布后如何获取jar包所在目录路径

    这篇文章主要介绍了SpringBoot项目jar发布后如何获取jar包所在目录路径,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • Java后端登录实现返回token

    Java后端登录实现返回token

    本文主要介绍了Java后端登录实现返回token,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07

最新评论