多线程代码长期以来一直是服务器开发人员的毒药(问问Oracle的Java语言架构师和并行开发大师BrianGoetz)。Java的核心库不断增加各种复杂的用法来减少访问共享资源时的线程等待时间。其中之一是经典的ReadWriteLock,它允许您将代码分成两部分:需要互斥的写入操作和不需要的读取操作。从表面上看,它看起来很棒。问题是读写锁可能会非常慢(最多10次),这有悖于它的初衷。Java8引入了一种新型的读写锁——称为时间戳锁。好消息是这家伙真的非常非常快。坏消息是它使用起来更复杂,需要处理更多的状态。而且它是不可重入的,这意味着一个线程可能会自己死锁。时间戳锁有一个“乐观”模式。在这种模式下,每次加锁操作都会返回一个时间戳作为某种权限凭证;每个解锁操作都需要提供其对应的时间戳。如果线程请求的写锁恰好已被读锁持有,则读解锁将失效(因为时间戳已过期)。这时,应用程序需要从头开始,或许使用悲观模式锁(也实现了时间戳锁)。这一切都需要你自己去想,一个时间戳只能解开它对应的锁——这得非常小心。我们来看一个这种锁的例子-longstamp=lock.tryOptimisticRead();//非阻塞路径-超快work();//我们希望此时不会发生写操作if(lock.validate(stamp)){//成功!没有写操作干扰}else{//肯定有另外一个线程同时获取了写操作锁,改变时间戳//懒人说——换一个更昂贵的锁吧stamp=lock.readLock();//这是传统的读操作锁,会阻塞try{//现在不可能发生写操作work();}finally{lock.unlock(stamp);//使用对应的时间戳解锁}}并发加法器Java8的另一个重要特性是并发“加法器”,这对于大规模运行的代码特别有趣。最基本的并发模式之一是读写计数器。因此,今天有很多方法可以处理这个问题,但没有一种方法像Java8提供的那样高效或优雅。到目前为止,这个问题已经用原子类(Atomics)解决了,它直接使用CPU的“比较和交换”指令(CAS)来测试和设置计数器的值。问题在于,当CAS指令因竞争而失败时,AtomicInteger类将死亡并等待,在无限循环中尝试CAS指令,直到成功。在争用概率很高的环境中,这种实施已被证明非常缓慢。看看Java8的LongAdder。该系列类为并行读取和写入大量值的代码提供了方便的解决方案。它超级容易使用。只需初始化一个LongAdder对象并使用其add()和intValue()方法来递增和采样计数器。这个和旧的Atomic类的区别在于,当CAS指令竞争失败时,Adder不会一直占用CPU,而是为当前线程分配一个内部cell对象来存储计数器的增量。然后将该值与其他挂起的单元格对象一起添加到intValue()的结果中。这减少了重复使用CAS指令或阻塞其他线程的可能性。如果你问自己,什么时候应该使用并发加法器而不是原子类来管理计数器?简单的答案是——一直这样做。并行排序正如并发加法器加速计数一样,Java8也实现了一种巧妙的方法来加速排序。秘诀很简单。你不再这样做:Array.sort(myArray);但是这样做:Arrays.parallelSort(myArray);这会自动将目标数组分成几个部分,放在独立的CPU核心上运行,然后合并结果。这里需要注意的是,在大量使用多线程的环境中,例如繁忙的Web容器,随着越来越多的CPU上下文切换增加开销,这种方法的好处会减少(超过90%)。切换到新的日期接口Java8引入了全新的日期时间接口。当前接口的大部分方法已被标记为已弃用,您知道是时候引入新接口了。新的日期接口为Java核心库带来了易用性和准确性,这在以前只有Joda时间才能实现。图书馆更友好,更容易管理)。与任何新界面一样,好消息是界面变得更加优雅和强大。但遗憾的是仍有大量代码使用旧接口,短时间内不会改变。为了连接新旧接口,历史悠久的Date类添加了一个toInstant()方法,将Date转换为新的表示形式。当您既想享受新界面的好处,又想享受那些只接受旧日期表示的好处时,这种方法特别有效。控制OS进程想要在代码中启动OS进程,可以通过JNI调用来实现-但这有点神秘,您可能会得到意想不到的结果,并且沿途会出现一些非常糟糕的结果.例外。即便如此,也是无法避免的。但是进程还有一个烦人的特性——搞不好就会变成僵尸进程。从Java运行进程的当前问题是一旦进程启动就很难控制它。为了帮助我们解决这个问题,Java8在Process类中引入了三个新方法destroyForcibly——结束一个进程,成功率比以前高很多。isAlive-检查您启动的进程是否存在。重载waitFor(),您现在可以指定等待进程结束的时间。这个接口会在进程退出成功后返回,如果超时也会返回,因为你可能要手动终止它。这里有两个很好的例子来说明如何使用这些新方法——如果进程没有在规定的时间内退出,就杀死它并继续。if(process.wait(MY_TIMEOUT,TimeUnit.MILLISECONDS)){//success}else{process.destroyForcibly();}在代码结束之前,确保所有进程都已退出。僵尸进程会逐渐耗尽系统资源。for(Processp:processes){if(p.isAlive()){p.destroyForcibly();}}精确的数字操作数字溢出会导致一些讨厌的错误,因为它本质上是无错误的。在一些系统中,整数值是不断递增的(比如计数器),溢出问题尤为严重。在这些情况下,产品在进化阶段运行良好,即使在商业使用后的很长一段时间内也是如此,但最终会因为操作开始溢出而奇怪地失败,产生完全不可预测的价值。为了解决这个问题,Java8向Math类添加了几个新的“精度”方法,通过抛出UncheckedArithmeticException异常来保护重要代码不被溢出。intsafeC=Math.multiplyExact(bigA,bigB);//如果结果超过+-2^31,将抛出ArithmeticException。坏处是你必须找出可能溢出的代码。无论如何,没有自动解决方案。但我认为拥有这些接口总比没有好。安全随机数生成器过去几年,Java一直因安全漏洞而受到抨击。不管是否合理,Java已经做了很多工作来加强虚拟机和框架层以抵御攻击。如果随机数来自随机性较低的种子,则使用随机数生成密钥或散列敏感信息的系统更容易受到攻击。直到现在,随机数生成算法都留给了开发人员。但问题是,如果你想要的算法依赖于特定的硬件、操作系统、虚拟机,那么你不一定能实现它。在这种情况下,应用程序倾向于使用较弱的默认生成器,这使它们面临更大的风险。Java8增加了一个名为SecureRandom.getInstanceStrong()的新方法,其目标是让虚拟机为您选择一个安全的随机数生成器。如果你的代码没有对操作系统、硬件、虚拟机的完全控制(如果你的程序部署到云端或者PaaS上,这很常见),我建议你认真考虑使用这个接口。可选地取消引用空指针就像“踢你的脚趾”——自从你学会走路以来它就一直伴随着你,无论你现在多么聪明——你仍然会犯这个错误。为了帮助解决这个老问题,Java8引入了一个名为Optional
