字节跳动面试官:i++ 是线程安全的吗?

go中数组、切片、map是否线程(并发)安全? 李某人带你了解go中数组、切片、map的线程安全(面试版) 阅读详情

点击“开发者技术前线”,选择“星标”

在看|星标|留言,  真爱

转自:zemliu

链接:cnblogs.com/zemliu/p/3298685.html

背景

最近去面字节跳动,除了算法之外,问到了一个基础题,关于i++ 是否线程安全?今天分享给大家。

简介

volatile关键字保证了在多线程环境下,被修饰的变量在别修改后会马上同步到主存,这样该线程对这个变量的修改就是对所有其他线程可见的,其他线程能够马上读到这个修改后值.

Thread的本地内存

  • 每个Thread都拥有自己的线程存储空间

  • Thread何时同步本地存储空间的数据到主存是不确定的

例子

借用Google JEREMY MANSON 的解释,上图表示两个线程并发执行,而且代码顺序上为Thread1->Thread2

1、不用 volatile

假如ready字段不使用volatile,那么Thread 1对ready做出的修改对于Thread2来说未必是可见的,是否可见是不确定的.假如此时thread1 ready泄露了(leak through)了,那么Thread 2可以看见ready为true,但是有可能answer的改变并没有泄露,则thread2有可能会输出 0 (answer=42对thread2并不可见)

2、使用 volatile

使用volatile以后,做了如下事情

  • 每次修改volatile变量都会同步到主存中

  • 每次读取volatile变量的值都强制从主存读取最新的值(强制JVM不可优化volatile变量,如JVM优化后变量读取会使用cpu缓存而不从主存中读取)

  • 线程 A 中写入 volatile 变量之前可见的变量, 在线程 B 中读取该 volatile 变量以后, 线程 B 对其他在 A 中的可见变量也可见. 换句话说, 写 volatile 类似于退出同步块, 而读取 volatile 类似于进入同步块

所以如果使用了volatile,那么Thread2读取到的值为read=>true,answer=>42,当然使用volatile的同时也会增加性能开销

注意

volatile并不能保证非源自性操作的多线程安全问题得到解决,volatile解决的是多线程间共享变量的可见性问题,而例如多线程的i++,++i,依然还是会存在多线程问题,它是无法解决了.如下:使用一个线程i++,另一个i--,最终得到的结果不为0

public class VolatileTest {


    private static volatile int count = 0;
    private static final int times = Integer.MAX_VALUE;


    public static void main(String[] args) {


        long curTime = System.nanoTime();


        Thread decThread = new DecThread();
        decThread.start();


        // 使用run()来运行结果为0,原因是单线程执行不会有线程安全问题
        // new DecThread().run();


        System.out.println("Start thread: " + Thread.currentThread() + " i++");


        for (int i = 0; i < times; i++) {
            count++;
        }


        System.out.println("End thread: " + Thread.currentThread() + " i--");


        // 等待decThread结束
        while (decThread.isAlive());


        long duration = System.nanoTime() - curTime;
        System.out.println("Result: " + count);
        System.out.format("Duration: %.2fs\n", duration / 1.0e9);
    }


    private static class DecThread extends Thread {


        @Override
        public void run() {
            System.out.println("Start thread: " + Thread.currentThread() + " i--");
            for (int i = 0; i < times; i++) {
                count--;
            }
            System.out.println("End thread: " + Thread.currentThread() + " i--");
        }
    }
}

最后输出的结果是

“

Start thread: Thread[main,5,main] i++Start thread: Thread[Thread-0,5,main] i--End thread: Thread[main,5,main] i--End thread: Thread[Thread-0,5,main] i--Result: -460370604Duration: 67.37s

原因是i++和++i并非原子操作,我们若查看字节码,会发现

void f1() { i++; }

的字节码如下

void f1();
Code:
0: aload_0
1: dup
2: getfield #2; //Field i:I
5: iconst_1
6: iadd
7: putfield #2; //Field i:I
10: return

可见i++执行了多部操作, 从变量i中读取读取i的值 -> 值+1 -> 将+1后的值写回i中,这样在多线程的时候执行情况就类似如下了

Thread1             Thread2
r1 = i;             r3 = i;
r2 = r1 + 1;        r4 = r3 + 1;
i = r2;             i = r4;

这样会造成的问题就是 r1, r3读到的值都是 0, 最后两个线程都将 1 写入 i, 最后 i 等于 1, 但是却进行了两次自增操作

可知加了volatile和没加volatile都无法解决非原子操作的线程同步问题

线程同步问题的解决

Java提供了java.util.concurrent.atomic 包来提供线程安全的基本类型包装类,例子如下

public class SafeTest {


    private static AtomicInteger count = new AtomicInteger(0);
    private static final int times = Integer.MAX_VALUE;


    public static void main(String[] args) {


        long curTime = System.nanoTime();


        Thread decThread = new DecThread();
        decThread.start();


        // 使用run()来运行结果为0,原因是单线程执行不会有线程安全问题
        // new DecThread().run();


        System.out.println("Start thread: " + Thread.currentThread() + " i++");


        for (int i = 0; i < times; i++) {
            count.incrementAndGet();
        }


        // 等待decThread结束
        while (decThread.isAlive());


        long duration = System.nanoTime() - curTime;
        System.out.println("Result: " + count);
        System.out.format("Duration: %.2f\n", duration / 1.0e9);
    }


    private static class DecThread extends Thread {


        @Override
        public void run() {
            System.out.println("Start thread: " + Thread.currentThread() + " i--");
            for (int i = 0; i < times; i++) {
                count.decrementAndGet();
            }
            System.out.println("End thread: " + Thread.currentThread() + " i--");
        }
    }
}

输出

“

Start thread: Thread[main,5,main] i++Start thread: Thread[Thread-0,5,main] i--End thread: Thread[Thread-0,5,main] i--Result: 0Duration: 105.15

结论

  1. volatile解决了线程间共享变量的可见性问题

  2. 使用volatile会增加性能开销

  3. volatile并不能解决线程同步问题

  4. 解决i++或者++i这样的线程同步问题需要使用synchronized或者AtomicXX系列的包装类,同时也会增加性能开销

最后福利:

在这里,我为大家准备了一份2020年最新最全的面试题及答案,这套电子书涵盖了诸多后端,客户端,前端技术栈的面试题和答案,相信可以帮助大家在最短的时间内复习的大多数面试题,从而拿到自己心仪的offer。

前线推出学习交流群,加群一定要备注:研究/工作方向+地点+学校/公司+昵称(如java+上海+上交+可可)根据格式备注,可更快被通过且邀请进群,领取一份专属学习礼包扫码加我微信进群,大厂内推和技术交流,大佬们零距离

好文点个在看吧!

字节网上面经整理(100道面试题)一篇 要理解闭包,必须要知道js变量是有局部变量和全局变量,全局变量在函数内外都可以访问到,但是局部变量,在函数内可以访问,函数外就不可以访问了,如果我们必须要拿到函数f1内部的变量,我们可以在函数内部再定义一个函数f2,根据Js的作用域链,f2这个函数可以访问到f1的变量,但是f1访问不到f2的,这时,我们再返回f2,这样以来,我们在外部调用f1函数,就可以访问到其内部的变量了,这就形成了闭包。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到他们。 阅读详情

相关推荐

【毕业求职季】-听说你想去大厂看学妹,带你看看字节跳动抖音电商后端面试长啥样?

【一面】 注:面试官特别友善,面试体验特别好 自我介绍 实习时间挺长? 数组和链表的区别? ArrayList的扩容是怎么实现? 介绍一下hashmap,介绍一下put操作做了哪些步骤?(问了细节) hashmap判定重复是==还是equals hashmap1.7和1.8? hashMap是线程安全吗?介绍一下concurrentHashMap?如何保证线 程安全的同时保证吞吐量? synchronized在jdk

wenyusuran的专栏 3562

2024届【校招】安全面试题和岗位总结(字节、百度、腾讯、美团等大厂)

个人强烈感觉面试因人而异,对于简历上有具体项目经历的同学,个人感觉面试官会着重让你介绍自己的项目,包括但不限于介绍一次真实攻防/渗透/挖洞/CTF/代码审计的经历 => 因此对于自己的项目,面试前建议做一次复盘,最好能用文字描述出细节,在面试时才不会磕磕绊绊、或者忘了一些自己很得意的细节面试题会一直更新(大概,直到我毕业或者躺平为止吧...)包括一些身边同学(若他们同意的话)和牛客上扒拉下来的(若有,会贴出链接)还有自己的一些经历。

Python_0011的博客 2万+

C++多线程断点续传下载器:从HTTP Range到线程安全的工业级实现

在并发编程和网络应用开发中,多线程文件下载是提升传输效率的核心技术。其基本原理是利用HTTP协议的Range请求头,将大文件分割为多个数据块,通过多个线程并行下载,最后按序组装。这项技术的核心价值在于充分利用带宽资源,显著缩短大文件传输时间,同时通过状态持久化机制实现断点续传,增强下载任务的鲁棒性。典型的应用场景包括软件分发平台、云存储客户端、大数据处理中的文件拉取等。实现一个健壮的下载器需要深入理解线程同步、网络协议和文件I/O的交互,其中线程安全的数据写入和进度管理是关键挑战。本文以C++为例,详细拆解

weixin_30776545的博客 354

linkedlist和arraylist的区别是什么

主要区别:ArrayList是Array(动态数组)的数据结构,而LinkedList是Link(链表)的数据结构。随机访问(get和set)时,ArrayList优于LinkedList;新增和删除操作,LinedList比较占优势。 LinkedeList和ArrayList的区别 1、数据结构不同 ArrayList是Array(动态数组)的数据结构,LinkedList是Link(链表)的...

thunder-1 2097

字节跳动Java基础专项面试:ArrayList扩容、volatile可见性、泛型擦除,道道有坑

本文聚焦字节跳动Android岗Java基础面试题,从源码层面剖析7个高频考点。内容包括:ArrayList扩容机制(1.5倍增长)、volatile与synchronized区别(可见性/有序性vs原子性)、JVM内存模型(堆栈分区)、泛型擦除本质、synchronized锁升级流程、ThreadLocal实现原理及内存泄漏风险、深/浅拷贝应用场景。文章强调大厂面试注重实现细节而非概念背诵,建议阅读JDK源码(如HashMap红黑树转换)加深理解,并预告下期将探讨Kotlin协程等进阶话题。

郑伟强dev的专栏 43

别再只用线性回归了!用sklearn的核岭回归搞定非线性数据(附RBF核调参实战)

本文详细介绍了如何使用sklearn的核岭回归处理非线性数据,特别是RBF核的应用与调参实战。通过网格搜索找到最佳参数组合,核岭回归能有效捕捉数据中的复杂波动,显著提升模型性能。适合需要解决非线性问题的数据科学家和机器学习工程师。

weixin_34246529的博客 336

热乎的面经:为了拿到字节跳动offer,鬼知道我经历了啥...

点击上方“码农突围”,马上关注这里是码农充电第一站,回复“666”,获取一份专属大礼包真爱,请设置“星标”或点个“在看”作者:不穿格子衫的Java程序猿来源:https://url.cn...

欢迎关注公众号:【码农突围】,回复9999,可以获取一份LeetCode刷题笔记 4957

采用LM324输出方波、正弦波、三角波

介绍以LM324运放芯片为核心,设计电路连接,实现三角波转化为正弦波,再由正弦波转化为方波输出的简易函数信号发生器的基本功能。本文介绍通过使用NI Multisim 14.0软件仿真模拟搭建所设计的电路,再使用这个软件的示波器进行仿真输出三角波,正弦波,方波。使用软件进行仿真后再到实验室进行调试,将其接入电源,并通过在显示器上观察波形以及数据,得到结果。该电路结构简单,信号的幅度、频率等参数可调,在仿真软件和实验室检测都有较好的性能输出,可替代实验室标准的函数信号发生器完成一般的实验要求。

qq_48771816的博客 7万+

java中级开发面试,字节跳动8年老Java面试官经验谈

很多程序员,整天沉浸在业务代码的 CRUD 中,业务中没有大量数据做并发,缺少实战经验,对并发仅仅停留在了解,做不到精通,所以总是与大厂擦肩而过。我把私藏的这套并发体系的笔记和思维脑图分享出来,理论知识与项目实战的结合,我觉得只要你肯花时间用心学完这些,一定可以快速掌握并发编程。总是与大厂擦肩而过。我把私藏的这套并发体系的笔记和思维脑图分享出来,理论知识与项目实战的结合,我觉得只要你肯花时间用心学完这些,一定可以快速掌握并发编程。本文已被。

m0_61408947的博客 574

从原理到应用:深入解析双编码器在机器人关节力矩感知中的实现

本文深入解析了双编码器方案在机器人关节力矩感知中的实现原理与应用。该方案通过在电机侧和输出侧分别安装编码器,利用两者位置差异间接高精度计算外力,克服了传统电流环精度不足和扭矩传感器成本高昂的缺点。它为实现协作机器人的丝滑拖动示教、灵敏碰撞检测以及足式机器人的动态平衡与柔顺控制提供了关键技术支撑,是目前高性能机器人力控的主流选择。

t8u9v0w1x的博客 661

YOLOv11 网络结构详解:一文看懂 Backbone、Head、C3k2、SPPF 和 Detect 检测头

在使用 YOLOv11 进行目标检测时,我们经常会接触到或这样的模型配置文件。backbone和head分别是什么意思?ConvC3k2SPPFC2PSA这些模块分别起什么作用?P3/8、P4/16、P5/32 代表什么?为什么最后 Detect 要同时接收 3 个不同尺度的特征图?YOLOv11n、YOLOv11s、YOLOv11m、YOLOv11l、YOLOv11x 的区别在哪里?

learnerYC的博客 1809

字节跳动后端C++面试

后端商业化面试 - 字节跳动好喜欢问网络

Wangzc_1116的博客 588

西门子博途系列学习笔记SCL(三)______自由口通讯(RS485轮询程序)

西门子博途V16系列学习笔记SCL(三)___自由口通讯轮询程序(RS485) 西门子博途系列学习笔记SCL(三)______自由口通讯(RS485轮询程序) 文章目录西门子博途V16系列学习笔记SCL(三)___自由口通讯轮询程序(RS485)前言一、FreePort自由口通讯是什么?二、使用步骤1.模块参数定义2.定义FB功能块参数:3.程序代码如下(每天写代码100行,学不会你来找我):总结 前言 很长时间都没有更新了,主要是最近在学习罗克韦尔的STUDIO5000PLC编程软件,怎么说,和博

weixin_42940283的博客 1万+

从字节跳动面试中快速胜出:五天三面,这份Java面试笔记功不可没

需要完整版PDF学习资源请扫描下方名片100%获取免费下载方式!👇👇。

资料免费分享,点击名片 1096

C++和OpenGL实现3D游戏编程【连载7】——文字和汉字的显示(附源码)

上一节我们讨论了纹理在二维平面内不规则图形贴图的相关基础操作,本节我们开始了解游戏里文字以及汉字的显示方法。本节课我们将从基本的ASCII字符显示,拓展到中文字符的显示,最后再讲到纹理字符的显示,并对各种文字显示方法的优缺点和使用场景进行分析,这节课将使用到显示列表、兼容视图、屏幕图形转纹理等操作。

zhooyu的博客 1498

机器人中的数值优化(一)—— 数学优化、凸集合与凸函数

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考,主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等,本系列文章篇数较多,不定期更新,上半部分介绍无约束优化,下半部分介绍带约束的优化,中间会穿插一些路径规划方面的应用实例。

慕羽★的博客 2521

你没见过的全新版本!985研一成功拿到字节跳动offer上岸,附上面经及答案分享!

一:前言 大家好,我是洲洲,目前北京门头沟学院研一在读, 趁考试周之前的两个星期,找了一波实习,历时半个月左右 踩过很多坑,也有裸面被怼的哑口无言的时候 总的来说,体验是找大厂实习真的很难 投很多岗位就是没有后续,石沉大海,相信不止我一个人这样 ???? ???? 准备的过程中看了不少的帖子,现在回馈一下大家 ???? 发个帖子攒攒人品,许愿后续过过过!! ???? ???? 二:项目和实习部分 问项目相关: java的锁了解多少 悲观锁、乐观锁、可重入锁、中断锁、读锁、写锁、synchronized

weixin_53224039的博客 3477

Arduino、ESP32驱动AS3935数字闪电传感器(闪电传感器)

闪电传感器采用AMS独具创新的AS3935数字闪电传感芯片与Coilcraft的MA5532-AE专用天线,可在室内或室外,安全地检测方圆40km范围内发生闪电的距离,闪电相对强度与统计闪电发生次数。

不脱发的程序猿 2011

高效征服字节跳动:仅用半天三面五天即获offer,硬实力与独家Java面试笔记的完美结合

需要完整版PDF学习资源请扫描下方名片100%获取免费下载方式!👇👇。

资料免费分享,点击名片 879

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

字节半天*3面/5天拿offer,全凭自身硬实力和这份Java面试笔记

先说说大致情况把,当时是下午两点约的一面,没想到直接面到六点一次性面完了。。。 面试部门:北京的基础架构,后端开发 一面: 讲一讲hash表 hash冲突了怎么办?(拉链)除了拉链还有吗?(开放地址法,不是很了解,听过) 如果使用hash进行分布式存储有什么弊端?(可扩展性不强,例如增加节点和节点宕机的情况) 如何解决?(一致性hash,提到了redis的哈希槽)讲一讲吧 一致性hash会出现什么问题?(数据倾斜问题)如何解决?(虚拟节点) 介绍JVM(五大部分) 什么时候会G

m0_63213253的博客 121

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

2024年的大厂末班车!啃完这些资料,我拿到了字节跳动Android高级开发工程师的offer!

(离当初普遍说法中说的3面+HR面 貌似差距有点大¬_¬) 算法到 Boss 面都还在写,不过庆幸的是面试官没有为难我 (老实交代了算法没怎么准备,哎…)算法都不太难。整体项目比基础问得多。算法、基础是敲门砖,项目是试金石,良好的面试形象是加分项。 (形象还是要的,起码要整洁不邋遢)简历上列举的项目多想想,为什么做这个项目?做这个项目的目标是什么?我的方案是什么?相对其他方案我的方案优势是什么?项目的收益是什么?项目的架构图是否能画出来?项目中使用的主要框架原理是否前前后后都清楚? (我大概就是项目拯救了自

2501_90193504的博客 334

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

2024应届毕业生,Android春招总结,已入职小米(附面试题答案)

由于劣势1,2,6,我的简历很难写的出彩,也很难走内推通道。由于劣势3,4,5,在面试时缺少亮点和深度,无法让面试官留下太深的印象。由于优势1,2,我在聊个人经历时有很多可以说的内容,可以充分展现我的业务能力和开发基础。

2401_86391940的博客 1670

字节跳动面经汇总

目录 6.你的学习目标是什么?还想学习什么技术? 重写与重载。 C++虚函数表,只有虚函数表的对象占据多少内存。 SSO原理。 SSL原理。 访问一个网页,响应很慢,从哪些方面排查问题?如何优化? I/O多路复用与其他网络I/O模型的区别。 select、poll、epoll C++内存分布。 Define与const。 情景题:某脚本同时处理一大堆文件,若脚本突然挂掉,如何判断处理到了...

windy杨树的博客 367

“出身双非学校,大厂offer我也拿得下!”3个月系统复习入职头条Android研发岗,确定方向真的比努力更重要!

找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。多练。(视频优势是互动感强,容易集中注意力)你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。以上就是总结的关于在面试的一些总结,希望对大家能有些帮助,除了这些面试中需要注意的问题,当然最重要的就是刷题了,这里放上我之前整理的一份超全的面试专题PDF还有。

2401_84121827的博客 770

面试官:为什么不推荐使用 uuid 作为 mysql 主键?

点击上方关注 “终端研发部”设为“星标”,和你一起掌握更多数据库知识我们举个例子假设有一个用户表创建一个表,使用UUID作为主键:CREATE TABLE users_uuid ( id UUID PRIMARY KEY, username VARCHAR(255) );插入一百万行数据-- 使用UUID()函数生成UUID INSERT INTO users_uuid (id, ...

这个时代,作为程序员可能要学习小程序 443
上一篇: 计算机专业的学生也太惨了吧!“劝”你别学计算机
下一篇: 为什么 Java 中“1000==1000”为false,而”100==100“为true?
DevolperFront
博客等级 码龄8年 464粉丝 81原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值