代码精进之路
范学雷
Oracle首席软件工程师,Java SE安全组成员,OpenJDK评审成员
立即订阅
6350 人已学习
课程目录
已完结 47 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 你写的每一行代码,都是你的名片
免费
第一模块:代码“规范”篇 (16讲)
01 | 从条件运算符说起,反思什么是好代码
02 | 把错误关在笼子里的五道关卡
03 | 优秀程序员的六个关键特质
04 | 代码规范的价值:复盘苹果公司的GoToFail漏洞
05 | 经验总结:如何给你的代码起好名字?
06 | 代码整理的关键逻辑和最佳案例
07 | 写好注释,真的是小菜一碟吗?
08 | 写好声明的“八项纪律”
09 | 怎么用好Java注解?
10 | 异常处理都有哪些陷阱?
11 | 组织好代码段,让人对它“一见钟情”
12丨组织好代码文件,要有“用户思维”
13 | 接口规范,是协作的合约
14 | 怎么写好用户指南?
15 | 编写规范代码的检查清单
16丨代码“规范”篇用户答疑
第二模块:代码“经济”篇 (14讲)
17 | 为什么需要经济的代码?
18丨思考框架:什么样的代码才是高效的代码?
19 | 怎么避免过度设计?
20 | 简单和直观,是永恒的解决方案
21 | 怎么设计一个简单又直观的接口?
22丨高效率,从超越线程同步开始!
23 | 怎么减少内存使用,减轻内存管理负担?
24 | 黑白灰,理解延迟分配的两面性
25 | 使用有序的代码,调动异步的事件
26 | 有哪些招惹麻烦的性能陷阱?
27 | 怎么编写可持续发展的代码?
28 | 怎么尽量“不写”代码?
29 | 编写经济代码的检查清单
30丨“代码经济篇”答疑汇总
第三模块:代码“安全”篇 (14讲)
31 | 为什么安全的代码这么重要?
32 | 如何评估代码的安全缺陷?
33 | 整数的运算有哪些安全威胁?
34 | 数组和集合,可变量的安全陷阱
35 | 怎么处理敏感信息?
36 | 继承有什么安全缺陷?
37 | 边界,信任的分水岭
38 | 对象序列化的危害有多大?
39 | 怎么控制好代码的权力?
40 | 规范,代码长治久安的基础
41 | 预案,代码的主动风险管理
42 | 纵深,代码安全的深度防御
43 | 编写安全代码的最佳实践清单
44 | “代码安全篇”答疑汇总
加餐 (1讲)
Q&A加餐丨关于代码质量,你关心的那些事儿
结束语 (1讲)
结束语|如何成为一个编程好手?
代码精进之路
登录|注册

24 | 黑白灰,理解延迟分配的两面性

范学雷 2019-02-27
上一次,我们讨论了减少内存使用的两个大方向,减少实例数量和减少实例的尺寸。如果我们把时间的因素考虑在内,还有一些重要的技术,可以用来减少运行时的实例数量。其中,延迟分配是一个重要的思路。

延迟分配

在前面讨论怎么写声明的时候,为了避免初始化的遗漏或者不必要的代码重复,我们一般建议“声明时就初始化”。但是,如果初始化涉及的计算量比较大,占用的资源比较多或者占用的时间比较长,声明时就初始化的方案可能会占用不必要的资源,甚至成为软件的一个潜在安全问题。
这时候,我们就需要考虑延迟分配的方案了。也就是说,不到需要时候,不占用不必要的资源。
下面,我们通过一个例子来了解下什么是延迟分配,以及延迟分配的好处。
在 Java 核心类中,ArrayList 是一个可调整大小的列表,内部实现使用数组存储数据。它的优点是列表大小可调整,数组结构紧凑。列表大小可以预先确定,并且在大小不经常变化的情况下,ArrayList 要比 LinkedList 节省空间,所以是一个优先选项。
但是,一旦列表大小不能确定,或者列表大小经常变化,ArrayList 的内部数组就需要调整大小,这就需要内部分配新数组,废弃旧数组,并且把旧数组的数据拷贝到新数组。这时候,ArrayList 就不是一个好的选择了。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《代码精进之路》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(12)

  • yang
    1 通过采用java内存模型,保证多线程场景下共享资源的可见性
    2使用局部变量,可以减少主存与线程内存的拷贝次数
    3第一次是初始化,第二次是同步局部变量与属性变量的值,保持一致
    4第一次检查是为了快速获取对象,第二次检查是为了防止对象未初始化,就是标准的double check
    5是为了线程安全,同时高性能,锁范围更小化
    6前者是线程安全,后者是非线程安全
    7还是减少主存与线程内存值拷贝的开销
    个人理解,如有误,敬请指正

    作者回复: volatile的使用,需要一定程度的同步,也就是你说的拷贝开销。减少volatile变量的引用,可以提高效率。

    恭喜你,这些Java的难点你掌握的很扎实!

    2019-02-27
    19
  • 梦醒时分
    我的思考:
    1.volatile是用来保证变量的可见性的,这样其他线程才能及时看到变量的修改
    2.为啥要使用temporaryMap变量,这里没有想明白
    3.两次设置temporaryMap变量,目的是双重检查,防止进入同步代码块中,变量已被赋值了
    4.同上
    5.synchronized的使用是影响性能的,所有在使用它之前,先校验下是否需要进入同步块中
    6.ConcurrentHashMap是线程安全的,而HashMap不是线程安全的
    7.为啥使用temporaryMap.put不太清楚

    作者回复: 关于temporaryMap的使用,请参考@yang的留言。

    2019-02-27
    6
  • 拉可里啦
    作为类的全局变量而非静态变量,只能被类的实例所拥有,那么只有一个对象再操作这个全局变量,单线程操作这个变量不会有线程安全问题,多个线程同时操作这个变量有线程安全问题,是以对象为单位的。不知道我这样理解对不对,还请老师指点指点。

    作者回复: 是的,是以对象为单位的。 所以,你看Java的代码里,synchronized(this), this指的就是一个具体的对象。同一个类,实例化的对象不同,也不需要同步。

    2019-04-07
    3
  • 唐名之
    @yang回到第二点 使用局部变量,可以减少主存与线程内存的拷贝次数 这个点还是有点不明白能解释下嘛?
    2019-03-01
    2
  • Linuxer
    请问各位思考题中的volatile修饰后是不是就只能用concurrenthashmap?要不赋值给局部变量后主存和线程内存还是不同步

    作者回复: volatile修饰符和使用concurrent hash map关系不大。volatile修饰的是标志符,不是标志符指向的内容。

    2019-02-28
    2
  • Jxin
    1.类属性的调用和赋值全部走set和get方法。这种非静态且多次赋值的局部变量应该尽量避免。
    2.带锁初始化操作应该上移到get方法。至少从函数功能来看,我认为初始化应该是属于get的。
    3.我真不喜欢加锁和每次get都做判断,所以了解业务上下文,如果可以,我会直接给该属性做初始化。
    3.如果了解完上下文我对这个集合或则散列表的大小能有一定把我,我会尝试给定一个合适的初始化大小。

    作者回复: 👍都是很好的实践经验! 了解适用场景,是高效代码的关键。

    2019-05-28
    1
  • aguan(^・ェ・^)
    老师,我有一个疑问,思考题的代码,在多线程的情况下,如果第16行用tempHashmap.put,是会出现空指针的。因为cpu指令重排序,当线程1在执行new map的时候,可能cpu先给temp分配内存空间,对象还没实例化,这时候另外一个线程在第一个if的时候发现temp不为空(因为有地址,但地址里并没有实例化对象),接着去执行16行的代码,会出现空指针问题吧

    作者回复: tempHashmap是一个局部变量,不跨越线程。

    2019-03-06
    1
  • 鱼筱雨
    范老师,我总是有个疑惑,举个例子
    Map<String,String> m = new HashMap<>();
    在这段代码中,左边的泛型中会声明具体类型,而右边的泛型中往往是空的,而我在代码开发中右边的泛型都会和左边保持一致,这样做有什么问题吗?哪种更好一点?

    作者回复: 现在推荐使用的模式是声明时指定类型,使用时让编译器来自动匹配。对于上面的代码,也就是左边声明具体类型,而右边使用空指示符。这样做的主要目的是简化编码,避免不必要的失误和检验。

    2019-08-29
  • Jxin
    采用了临时变量,对象是否就失去了可视性和有序性的特性?这样在new操作时,由于虚拟机的编译优化,cpu时间片切换时不是可能会出现空指针报错吗?
    2019-06-10
  • aguan(^・ェ・^)
    恍然大悟,所以局部变量是解决双重检查重排序空指针问题的安全方法👍
    2019-03-07
  • 轻歌赋
    1.双检锁在多CPU情况下存在内存语义bug,通过volatile实现其内存语义
    2.单线程内存一致性语义
    3.多线程并发,存在一个线程先于其他线程设置值的情况
    4.多线程并发,检查helloworldsmap是否被其他线程赋值
    5.提高并发度
    6.代码体现这是一个多CPU多线程的环境,存在map被并发修改的情况,concurrenthashmap是线程安全的,而hashmap是线程不安全的所以使用前者
    7.不知道,感觉像是逻辑上比较顺
    2019-03-02
  • 多拉格·five
    这个就是类似于单例里边的双重检查写法吧
    2019-02-27
收起评论
12
返回
顶部