时间戳锁
一直以来,多线程代码是服务器开发人员的毒药(问问Oracle的Java语言架构师和并行开发大师Brian Goetz)。Java的核心库不断加入各种复杂的用法来减少访问共享资源时的线程等待时间。其中之一就是经典的读写锁(ReadWriteLock), 它让你把代码分成两部分:需要互斥的写操作和不需要互斥的读操作。
表面上看起来很不错。问题是读写锁有可能是极慢的(最多10倍),这已经和它的初衷相悖了。Java 8引入了一种新的读写锁——叫做时间戳锁。好消息是这个家伙真的非常快。坏消息是它使用起来更复杂,有更多的状态需要处理。并且它是不可重入的,这意味着 一个线程有可能跟自己死锁。
时间戳锁有一种“乐观”模式,在这种模式下每次加锁操作都会返回一个时间戳作为某种权限凭证;每次解锁操作都需要提供它对应的时间戳。如果一个线程 在请求一个写操作锁的时候,这个锁碰巧已经被一个读操作持有,那么这个读操作的解锁将会失效(因为时间戳已经失效)。这个时候应用程序需要从头再来,也许 要使用悲观模式的锁(时间戳锁也有实现)。你需要自己搞定这一切,并且一个时间戳只能解锁它对应的锁——这一点必须非常小心。
下面我们来看一下这种锁的实例——
long stamp = lock.tryOptimisticRead(); // 非阻塞路径——超级快 work(); // 我们希望不要有写操作在这时发生 if (lock.validate(stamp)){ //成功!没有写操作干扰 } else { //肯定同时有另外一个线程获得了写操作锁,改变了时间戳 //懒汉说——我们切换到开销更大的锁吧 stamp = lock.readLock(); //这是传统的读操作锁,会阻塞 try { //现在不可能有写操作发生了 work(); } finally { lock.unlock(stamp); // 使用对应的时间戳解锁 } }
并发加法器
Java 8另一个出色的功能是并发“加法器”,它对大规模运行的代码尤其有意义。一种最基本的并发模式就是对一个计数器的读写。就其本身而言,现今处理这个问题有很多方法,但是没有一种能比Java 8提供的方法高效或优雅。
到目前为止,这个问题是用原子类(Atomics)来解决的,它直接利用了CPU的“比较并交换”指令(CAS)来测试并设置计数器的值。问题在于 当一条CAS指令因为竞争而失败的时候,AtomicInteger类会死等,在无限循环中不断尝试CAS指令,直到成功为止。在发生竞争概率很高的环境 中,这种实现被证明是非常慢的。
来看Java 8的LongAdder。这一系列类为大量并行读写数值的代码提供了方便的解决办法。使用超级简单。只要初始化一个LongAdder对象并使用它的add()和intValue()方法来累加和采样计数器。
这和旧的Atomic类的区别在于,当CAS指令因为竞争而失败时,Adder不会一直占着CPU,而是为当前线程分配一个内部cell对象来存储 计数器的增量。然后这个值和其他待处理的cell对象一起被加到intValue()的结果上。这减少了反复使用CAS指令或阻塞其他线程的可能性。
如果你问你自己,什么时候应该用并发加法器而不是原子类来管理计数器?简单的答案就是——一直这么做。
并行排序
正像并发加法器能加速计数一样,Java 8还实现了一种简洁的方法来加速排序。这个秘诀很简单。你不再这么做:
Array.sort(myArray);
而是这么做:
Arrays.parallelSort(myArray);
这会自动把目标数组分割成几个部分,这些部分会被放到独立的CPU核上去运行,再把结果合并起来。这里唯一需要注意的是,在一个大量使用多线程的环境中,比如一个繁忙的Web容器,这种方法的好处就会减弱(降低90%以上),因为越来越多的CPU上下文切换增加了开销。
切换到新的日期接口
Java 8引入了全新的date-time接口。当前接口的大多数方法都已被标记为deprecated,你就知道是时候推出新接口了。新的日期接口为Java核 心库带来了易用性和准确性,而以前只能用Joda time才能达到这样的效果(译者注:Joda time是一个第三方的日期库,比Java自带的库更友好更易于管理)。
跟任何新接口一样,好消息是接口变得更优雅更强大。但不幸的是还有大量的代码在使用旧接口,这个短时间内不会有改变。
为了衔接新旧接口,历史悠久的Date类新增了toInstant()方法,用于把Date转换成新的表示形式。当你既要享受新接口带来的好处,又要兼顾那些只接受旧的日期表示形式的接口时,这个方法会显得尤其高效。
控制操作系统进程
想在你的代码里启动一个操作系统进程,通过JNI调用就能完成——但这个东西总令人一知半解,你很有可能得到一个意想不到的结果,并且一路伴随着一些很糟糕的异常。
即便如此,这是无法避免的事情。但进程还有一个讨厌的特性就是——它们搞不好就会变成僵尸进程。目前从Java中运行进程带来的问题是,进程一旦启动就很难去控制它。
为了帮我们解决这个问题,Java 8在Process类中引入了三个新的方法
- destroyForcibly——结束一个进程,成功率比以前高很多。
- isAlive——查询你启动的进程是否还活着。
- 重载了waitFor(),你现在可以指定等待进程结束的时间了。进程成功退出后这个接口会返回,超时的话也会返回,因为你有可能要手动终止它。
这里有两个关于如何使用这些新方法的好例子——
- 如果进程没有在规定时间内退出,终止它并继续往前走。
if (process.wait(MY_TIMEOUT, TimeUnit.MILLISECONDS)){ //成功 } else { process.destroyForcibly(); }
- 在你的代码结束前,确保所有的进程都已退出。僵尸进程会逐渐耗尽系统资源。
for (Process p : processes) { if (p.isAlive()) { p.destroyForcibly(); } }
精确的数字运算
数字溢出会导致一些讨厌的bug,因为它本质上不会出错。在一些系统中,整型值不停地增长(比如计数器),溢出的问题就尤为严重。在这些案例里面, 产品在演进阶段运行得很好,甚至商用后的很长时间内也没问题,但最终会出奇怪的故障,因为运算开始溢出,产生了完全无法预料的值。
为了解决这个问题,Java 8为Math类添加了几个新的“精确型”方法,以便保护重要的代码不受溢出的影响,它的做法是当运算超过它的精度范围的时候,抛出一个未检查的ArithmeticException异常。
int safeC = Math.multiplyExact(bigA, bigB); // 如果结果超出+-2^31,就会抛出ArithmeticException异常
唯一不好的地方就是你必须自己找出可能产生溢出的代码。无论如何,没有什么自动的解决方案。但我觉得有这些接口总比没有好。
安全的随机数发生器
在过去几年中Java一直因为安全漏洞而饱受诟病。无论是否合理,Java已经做了大量工作来加强虚拟机和框架层,使之免受攻击。如果随机数来源于随机性不高的种子,那么那些用随机数来产生密钥或者散列敏感信息的系统就更易受攻击。
到目前为止,随机数发生算法由开发人员来决定。但问题是,如果你想要的算法依赖于特定的硬件、操作系统、虚拟机,那你就不一定能实现它。这种情况下,应用程序倾向于使用更弱的默认发生器,这就使他们暴露在更大的风险下了。
Java 8添加了一个新的方法叫SecureRandom.getInstanceStrong(),它的目标是让虚拟机为你选择一个安全的随机数发生器。如果你 的代码无法完全掌控操作系统、硬件、虚拟机(如果你的程序部署到云或者PaaS上,这是很常见的),我建议你认真考虑一下使用这个接口。
可选引用
空指针就像“踢到脚趾”一样——从你学会走路开始就伴随着你,无论现在你有多聪明——你还是会犯这个错。为了帮助解决这个老问题,Java 8引入了一个新模板叫Optional<T>。
这个模板是从Scala和Hashkell借鉴来的,用于明确声明传给函数或函数返回的引用有可能是空的。有了它,过度依赖旧文档或者看过的代码经常变动的人,就不需要去猜测某个引用是否可能为空。
Optional<User> tryFindUser(int userID) {
或
void processUser(User user, Optional<Cart> shoppingCart) {
Optional模板有一套函数,使得采样它更方便,比如isPresent()用来检查这个值是不是非空,或者ifPresent()你可以传递 一个Lambda函数过去,如果isPresent()返回true,这个Lambda函数就会被执行。不好的地方就跟Java 8的新日期接口一样,等这种模式逐渐流行,渗透到我们使用的库中和日常设计中,需要时间和工作量。
用新的Lambda语法打印Optional值:
value.ifPresent(System.out::print);
相关推荐
这就要谈及Java虚拟机(Java VirtualMachine,简称JVM)。JVM也是一个软件,不同的平台有不同的版本。我们编写的Java源码,编译后会生成一种.class文件,称为字节码文件。Java虚拟机就是负责将字节码文件翻译成特定...
### Java面试技巧详解 在IT行业中,特别是针对Java开发者的面试环节,良好的准备与恰当的表现极为关键。本文基于“java: 面试技巧”的标题和描述,将深入解析几个核心知识点,帮助求职者更好地应对Java相关的面试...
上次的教程中还没有来得及谈及的问题。我还将向你们展示 Javascript的许多神奇功能,使你能够制作真正对多媒体应用 程序。 以下是我们将涉及的内容: • 给变量赋值 • If-then语句 • For和while循环 • 编写你...
从给定的文件信息来看,虽然标题为"100道java面试",但实际上内容并未直接涉及Java技术的面试题目,而是围绕通用的面试技巧和常见问题展开。然而,我们可以从中提炼出一些与IT行业,尤其是Java开发相关的面试准备...
古今中外著名算题趣谈及详解
并且,对人才培养从“工程知识”“问题分析”“设计/开发解决方案”“研究”“使用现代工具”等12个方面提出了新的要求,其中,8个方面都谈及培养学生解决复杂工程问题的能力。 然后,本文分析了《Java框架...
在政府鼓励下,一系列体育产业政策出现,加之资本的投入使得优质的内容和商品大幅度的产生,以及居民健康意识的加强和参与大众体育的热情,使得体育产业进入了黄金发展期。大众健身作为体育产业的一部分,正如火如茶...
在Windows 8首次展示之际,IT人员就已经开始头疼系统升级、系统迁移、...大家关注了很多的功能,但笔者总结了以下十个很少被谈及的功能,并相信如果你使用了这些功能,会从中受益的。 1.快速启动 通过笔者的计
### 网上投递简历的经验之谈及心得 在当今社会,互联网已成为人们寻找工作机会的重要途径之一。本文将从网上投递简历的角度出发,总结一些实用的经验与心得,帮助求职者提高求职成功率。 #### 一、选择合适的招聘...
在Windows 8首次展示之际,IT人员就已经开始头疼系统升级、系统迁移、系统...大家关注了很多的功能,但笔者总结了以下十个很少被谈及的功能,并相信如果你使用了这些功能,会从中受益的。 1.快速启动 通过笔者的
【高考图文转换题例谈及训练】是针对高中语文考试中的一种题型进行解析和训练的文档,这种题型主要考查学生的分析能力和信息提取能力。图文转换题要求考生能够从图表中提取关键信息,并用简洁的语言进行概括。在解答...
谈及健身领域,最重要的两个因素就是健身场地和教练管理,在互联网时代下,专业的健身商品也成为企业发展重要的桎梏。2016年6月3日国务院印发的《全面健身计划(2016-2020年)》中提到:“不断扩大的健身人群、支持...
当谈及 dex 文件格式的详细结构时,dex 文件由一系列有序的数据块组成,主要包括以下几个部分: 1. dex 文件的整体布局,它定义了文件的起始点、各个数据区的偏移量、数据大小等。 2. header 区域,这是 dex 文件的...
磨剑系列专题报告:谈及财政预算时,需要知道哪些?(2020)(19页).pdf
后来渐渐参与各种项目,工作,等等原因,在初的7年中主要的开发语言是c++/c#,在这篇文章中我不想谈及c++,因为我在以往的博文中已经有很多这方面的随笔。虽然当时c#是我的主要开发语言,但是对于Java的每次重大...
《当我们谈及流动性的时候,我们在说什么?》这篇报告深入探讨了流动性在金融市场中的含义及其对股市和债市的影响。流动性在金融领域中具有多重含义,它既涉及到资产端,也涉及负债端,同时还涵盖宏观和微观层面。...
永远不要根据枚举的序数导出与它关联的值,而是要将它保存在一个实例域中:public enum Ensemble {Enum规范中谈及ordinal方法时写道: