关于java线程不安全问题的简述

本文简述Java线程不安全问题,介绍其表现为多线程操作静态变量结果不符预期。分析原因包括原子性、可见性和代码有序性问题。提出使用synchronized关键字解决,让代码满足三特性,还说明了多线程加锁操作的调度方式及性能消耗。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于java线程不安全问题的简述

什么是线程不安全及其具体解析
  1. 当我们执行一个Java.exe进程的时候,首先会初始化JVM参数,然后创建JVM虚拟机,再启动后台线程,最后执行就是执行我们代码行的main方法。

  2. 而在JVM运行的时候会将他管理的内存分为若干个区域,每一个线程都有其独有的程序计数器,java虚拟机栈和本地方法栈,以及线程共享的Java堆和方法区(包含运行时常量池)

  3. 当我们定义一个静态变量COUNT,它在被编译的时候创建于方法区。当我们创建多个线程去给COUNT执行++的操作的时候,我们最终所得到的COUNT值是不符合我们所期望的。

     private static int COUNT = 0;
        // 有一个变量COUNT=0;同时启动10个线程,每个线程循环1000次
        // 每次循环COUNT++
        // 每一个线程执行完毕之后打印COUNT
        public static void main(String[] args) throws InterruptedException {
            // 尽量同时启动
            Thread[] threads = new Thread[10];
    
            for (int i = 0; i < 10; i++) {
                threads[i] = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int j = 0; j < 1000; j++) {
                                COUNT++;
                        }
                    }
                });
            }
    
            for (Thread t:
                 threads) {
                t.start();
            }
    
            for (Thread t:
                 threads) {
                t.join();
            }
            System.out.println(COUNT);
        }
    

    当我们执行完这个代码的时候,它的结果往往小于10000.

    这就是所谓的线程不安全

  4. 当我们定义一个静态变量,它被创建于方法区,而每一个线程想要去修改静态变量COUNT的时候,他必须从方法区去获取到COUNT的值,然后在自己的线程私有区域去进行修改也就是++操作,修改完成之后再将COUNT的值写入方法区。但是线程是并发并行的,当线程1去获取并且COUNT的值之时,线程2可能在线程1没有写入的时候去获取COUNT的值,这就出现了线程不安全问题

线程不安全出现的原因
  1. 原子性。上面的代码是不具备原子性的。原子性就是提供互斥访问,在同一段时间只能有一个线程对COUNT进行操作
  2. 可见性。上述代码不具备可见性。可见性就是在一个线程COUNT进行操作的时候其他线程可以看见这个线程对COUNT的操作。
  3. 代码有序性。这一般是编译期和运行期代码优化而产生的问题,比如老总给员工下达可一个命令,你先去楼下买一杯奶茶,然后去工作一个小时,然后再去楼下买是个包子。笨人就是会严格按照老总说的来做,但聪明人就会想我先工作一个小时,然后买包子和奶茶,更省时间。JVM相当于聪明人,这就会产生问题了。
使用synchronized关键字解决线程不安全问题
  1. 对某一段代码加锁,然后让这段代码满足上述的三个特性:原子性,可见性和有序性。

  2. 其原理就是让多个线程间同步互斥,在一段时间内只有一个线程在对某一个变量进行操作。

  3. synchronized关键字加锁的对象一定是同一个对象。

    private static int COUNT = 0;
    
        public static void main(String[] args) throws InterruptedException {
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 10000 ; i++) {
                        synchronized (SynchronizedTest.class) {
                            COUNT++;
                        }
                    }
                }
            });
            t1.start();
    
    
            Thread t2 = new Thread((new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 10000; i++) {
                        synchronized (SynchronizedTest.class) {
                            COUNT++;
                        }
                    }
                }
            }));
    
            t2.start();
    
            t1.join();
            t2.join();
    
            System.out.println(COUNT);
    
        }
    
多线程加锁操作是如何实现调度的
  1. 线程处于运行态的时候申请对象锁,当获取到对象锁之后就会去执行同步代码,执行完之后释放对象锁。
  2. 线程处于运行态的时候申请对象锁,若是没有获取到对象锁,就会转变成阻塞态,等待获取对象锁成功的线程执行完毕释放对象锁,当这个对象锁被释放后,JVM就会通知获取对象锁失败的线程去竞争再次获取对象锁。
  3. 竞争获取对象锁的时候,线程处于运行态,竞争失败的话就会转成阻塞态。
  4. 加锁操作是很耗费CPU性能的,当线程状态改变的时候是处于内核态,而改变完成之后线程就处于用户态。这个是比较消耗CPU性能的。
2020/5月/15好上传最新版 JavaGuide 目前已经 70k+ Star ,目前已经是所有 Java 类别项目中 Star 数量第二的开源项目了。Star虽然很多,但是价值远远比上 Dubbo 这些开源项目,希望以后可以多出现一些这样的国产开源项目。国产开源项目!加油!奥利给! 随着越来越多的人参与完善这个项目,这个专注 “Java知识总结+面试指南 ” 项目的知识体系和内容的断完善。JavaGuide 目前包括下面这两部分内容: Java 核心知识总结; 面试方向:面试题、面试经验、备战面试系列文章以及面试真实体验系列文章 内容的庞大让JavaGuide 显的有一点臃肿。所以,我决定将专门为 Java 面试所写的文章以及来自读者投稿的文章整理成 《JavaGuide面试突击版》 系列,同时也为了更加方便大家阅读查阅。起这个名字也犹豫了很久,大家如果有更好的名字的话也可以向我建议。暂时的定位是将其作为 PDF 电子书,会像 JavaGuide 提供在线阅读版本。我之前也免费分享过PDF 版本的《Java面试突击》,期间一共更新了 3 个版本,但是由于后面难以同步和订正所以就没有再更新。《JavaGuide面试突击版》 pdf 版由于我工作流程的转变可以有效避免这个问题。 另外,这段时间,向我提这个建议的读者也一个两个,我自己当然也有这个感觉。只是自己一直没有抽出时间去做罢了!毕竟这算是一个比较耗费时间的工程。加油!奥利给! 这件事情具体耗费时间的地方是内容的排版优化(为了方便导出PDF生成目录),导出 PDF 我是通过 Typora 来做的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值