8 Great Java 8 Features No One's Talking about--转载

原文地址:http://www.infoq.com/articles/Java-8-Quiet-Features

If you haven’t seen some of the videos or tutorials around Java 8, you’ve probably been super-busy or have a more interesting social life than I do (which isn’t saying much). With new features like lambda expressions and Project Nashorn taking so much of the spotlight, I wanted to focus on some new APIs that have been a bit under the radar, but make Java 8 better in so many ways.

1. Stamped Locks

Multi-threaded code has long been the bane of server developers (just ask Oracle Java Language Architect and concurrency guruBrian Goetz). Over time complex idioms were added to the core Java libraries to help minimize thread waits when accessing shared resources. One of these is the classic ReadWriteLock that lets you divide code into sections that need to be mutually exclusive (writers), and sections that don’t (readers).

On paper this sounds great. The problem is that the ReadWriteLock can be super slow (up to 10x), which kind of defeats its purpose. Java 8 introduces a new ReadWrite lock – calledStampedLock. The good news here is that this guy is seriously fast. The bad news is that it’s more complicated to use and lugs around more state. It’s also not reentrant, which means a thread can have the dubious pleasure of deadlocking against itself.

StampedLock has an "optimistic" mode that issues a stamp that is returned by each locking operation to serve as a sort of admission ticket; each unlock operation needs to be passed its correlating stamp. Any thread that happens to acquire a write lock while a reader was holding an optimistic lock, will cause the optimistic unlock to be invalidated (the stamp is no longer valid). At that point the application can start all over, perhaps with a pessimistic lock (also implemented in StampedLock.) Managing that is up to you, and one stamp cannot be used to unlock another – so be super careful.

Let’s see this lock in action-

long stamp = lock.tryOptimisticRead(); // non blocking path - super fast
work(); // we‘re hoping no writing will go on in the meanwhile
if (lock.validate(stamp)){
       //success! no contention with a writer thread
}
else {
       //another thread must have acquired a write lock in the meanwhile, changing the stamp. 
       //bummer - let‘s downgrade to a heavier read lock

            stamp = lock.readLock(); //this is a traditional blocking read lock
       try {
                 //no writing happening now
                 work();

       }
       finally {
            lock.unlock(stamp); // release using the correlating stamp
       }
}

2. Concurrent Adders

Another beautiful addition to Java 8, meant specifically for code running at scale, is the concurrent “Adders”. One of the most basic concurrency patterns is reading and writing the value of a numeric counter. As such, there are many ways in which you can do this today, but none so efficient or elegant as what Java 8 has to offer.

Up until now this was done using Atomics, which used a direct CPU compare and swap (CAS) instruction (via the sun.misc.Unsafe class) to try and set the value of a counter. The problem was that when a CAS failed due to contention, the AtomicInteger would spin, continually retrying the CAS in an infinite loop until it succeeded. At high levels of contention this could prove to be pretty slow.

Enter Java 8’sLongAdders. This set of classes provides a convenient way to concurrently read and write numeric values at scale. Usage is super simple. Just instantiate a new LongAdder and use itsadd() andintValue() methods to increase and sample the counter.

The difference between this and the old Atomics is that here, when a CAS fails due to contention, instead of spinning the CPU, the Adder will store the delta in an internal cell object allocated for that thread. It will then add this value along with any other pending cells to the result of intValue(). This reduces the need to go back and CAS or block other threads.

If you’re asking yourself when should I prefer to use concurrent Adders over Atomics to manage counters? The simple answer is – always.

3. Parallel Sorting

Just as concurrent Adders speed up counting, Java 8 delivers a concise way to speed up sorting. The recipe is pretty simple. Instead of -

Array.sort(myArray);

You can now use –

Arrays.parallelSort(myArray);

This will automatically break up the target collection into several parts, which will be sorted independently across a number of cores and then grouped back together. The only caveat here is that when called in highly multi-threaded environments, such as a busy web container, the benefits of this approach will begin to diminish (by more than 90%) due to the cost of increased CPU context switches.

4. Switching to the new Date API

Java 8 introduces a complete new date-time API.  You kind of know it’s about time when most of the methods of the current one are marked as deprecated... The new API brings ease-of-use and accuracy long provided by the popular Joda time API into the core Java library.

As with any new API the good news is that it’s more elegant and functional. Unfortunately there are still vast amounts of code out there using the old API, and that won’t change any time soon.

To help bridge the gap between the old and new API’s, the venerable Date class now has a new method calledtoInstant() which converts the Date into the new representation. This can be especially effective in those cases where you‘re working on an API that expects the classic form, but would like to enjoy everything the new API has to offer.

5. Controlling OS Processes

Launching an OS process from within your code is right there with JNI calls – it’s something you do half-knowing there’s a good chance you’re going to get some unexpected results and some really bad exceptions down the line.

Even so, it’s a necessary evil. But processes have another nasty angle to them - they have a tendency to dangle. The problem with launching process from within Java code so far has been that is was hard to control a process once it was launched.

To help us with this Java 8 introduces three new methods in the Process class -

  1. destroyForcibly - terminates a process with a much higher degree of success than before.
  2. isAlive tells if a process launched by your code is still alive.
  3. A new overload for waitFor() lets you specify the amount of time you want to wait for the process to finish. This returns whether the process exited successfully or timed-out in which case you might terminate it.

Two good use-cases for these new methods are -

  • If the process did not finish in time, terminate and move forward:
if (process.wait(MY_TIMEOUT, TimeUnit.MILLISECONDS)){
       //success! }
else {
    process.destroyForcibly();
}
  • Make sure that before your code is done, you‘re not leaving any processes behind. Dangling processes can slowly but surely deplete your OS.
for (Process p : processes) {
       if (p.isAlive()) {
             p.destroyForcibly();
       }
}

6. Exact Numeric Operations

Numeric overflows can cause some of the nastiest bugs due to their implicit nature. This is especially true in systems where int values (such as counters) grow over time. In those cases things that work well in staging, and even during long periods in production, can start breaking in the weirdest of ways, when operations begin to overflow and produce completely unexpected values.

To help with this Java 8 has added severalnew “exact” methods to the Math class geared towards protecting sensitive code from implicit overflows, by throwing an unchecked ArithmeticException when the value of an operation overflows its precision.

int safeC = Math.multiplyExact(bigA, bigB); // will throw ArithmeticException if result exceeds +-2^31

The only downside is that it’s up to you to find those places in your code where overflows can happen. Not an automagical solution by any stretch, but I guess it’s better than nothing.

7. Secure Random Generation

Java has been under fire for several years for having security holes. Justified or not, alot of work has been done to fortify the JVM and frameworks from possible attacks. Random numbers with a low-level of entropy make systems that use random number generators to create encryption keys or hash sensitive information more susceptible to hacking.

So far selection of the Random Number Generation algorithms has been left to the developer. The problem is that where implementations depend on specific hardware / OS / JVM, the desired algorithm may not be available. In such cases applications have a tendency to default to weaker generators, which can put them at greater risk of attack.

Java 8 has added a new method calledSecureRandom.getInstanceStrong() whose aim is to have the JVM choose a secure provider for you. If you’re writing code without complete control of the OS / hardware / JVM on which it would run (which is very common when deploying to the cloud or PaaS), my suggestion is to give this approach some serious consideration.

8. Optional References

NulPointers are like stubbing your toes - you’ve been doing it since you could stand up, and no matter how smart you are today - chances are you still do. To help with this age-old problem Java 8 is introducing a new template called Optional<T>.

Borrowing from Scala and Haskell, this template is meant to explicitly state when a reference passed to or returned by a function can be null. This is meant to reduce the guessing game of whether a reference can be null, through over-reliance on documentation which may be out-of-date, or reading code which may change over time.

Optional<User> tryFindUser(int userID) {

or -

void processUser(User user, Optional<Cart> shoppingCart) {

The Optional template has a set of functions that make sampling it more convenient, such asisPresent()to check if an non-null value is available, or  ifPresent() to which you can pass a Lambda function that will be executed if isPresent is true. The downside is that much like with Java 8’s new date-time APIs, it will take time and work till this pattern takes hold and is absorbed into the libraries we use and design everyday.

New Lambda syntax for printing an optional value -

value.ifPresent(System.out::print);

About the Author

Tal Weiss is the CEO of Takipi. Tal has been designing scalable, real-time Java and C++ applications for the past 15 years. He still enjoys analyzing a good bug though, and instrumenting Java code. In his free time Tal plays Jazz drums.

8 Great Java 8 Features No One's Talking about--转载

时间: 2024-11-06 01:58:53

8 Great Java 8 Features No One's Talking about--转载的相关文章

Java 8 Features – The ULTIMATE Guide--reference

Now, it is time to gather all the major Java 8 features under one reference post for your reading pleasure. Enjoy! Table Of Contents 1. Introduction 2. New Features in Java language 2.1. Lambdas and Functional Interfaces 2.2. Interface Default and St

Java 内存泄漏--全解析和处理办法 [ 转载 ]

Java内存泄露——全解析和处理办法 [转载] @author 小筐子 @address http://www.jianshu.com/p/bf159a9c391a 本文章会一步一步的探讨内存泄露的问题.博主第一次书写长篇技术贴,如有错误或不周到的地方请多指教. JAVA是垃圾回收语言的一种,开发者无需特意管理内存分配.但是JAVA中还是存在着许多内存泄露的可能性,如果不好好处理内存泄露,会导致APP内存单元无法释放被浪费掉,最终导致内存全部占据堆栈(heap)挤爆进而程序崩溃. 内存泄露 说到

Java 理论与实践: 非阻塞算法简介--转载

在不只一个线程访问一个互斥的变量时,所有线程都必须使用同步,否则就可能会发生一些非常糟糕的事情.Java 语言中主要的同步手段就是synchronized 关键字(也称为内在锁),它强制实行互斥,确保执行 synchronized 块的线程的动作,能够被后来执行受相同锁保护的synchronized 块的其他线程看到.在使用得当的时候,内在锁可以让程序做到线程安全,但是在使用锁定保护短的代码路径,而且线程频繁地争用锁的时候,锁定可能成为相当繁重的操作. 在 “流行的原子” 一文中,我们研究了原子

《JAVA与模式》之中介者模式(转载)

原文出处:http://blog.csdn.net/zhengzhb/article/details/7430098   定义:用一个中介者对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使耦合松散,而且可以独立地改变它们之间的交互. 类型:行为类模式 类图: 中介者模式的结构 中介者模式又称为调停者模式,从类图中看,共分为3部分: 抽象中介者:定义好同事类对象到中介者对象的接口,用于各个同事类之间的通信.一般包括一个或几个抽象的事件方法,并由子类去实现. 中介者实现类:从抽象

Java 中extends与implements使用方法 (转载)

转自:http://blog.csdn.net/chen_chun_guang/article/details/6323201 初学Java语言, 代码中的extends和implements让我感到很迷惑,现在终于弄明白它们之间的区别和用法了. //定义一个Runner接口 public inerface Runner { int ID = 1; void run (); } //定义一个interface Animal,它继承于父类Runner interface Animal extend

java IO流文件的读写具体实例(转载)

引言: 关于java IO流的操作是非常常见的,基本上每个项目都会用到,每次遇到都是去网上找一找就行了,屡试不爽.上次突然一个同事问了我java文件的读取,我一下子就懵了第一反应就是去网上找,虽然也能找到,但自己总感觉不是很踏实,所以今天就抽空看了看java IO流的一些操作,感觉还是很有收获的,顺便总结些资料,方便以后进一步的学习... IO流的分类:1.根据流的数据对象来分:高端流:所有的内存中的流都是高端流,比如:InputStreamReader  低端流:所有的外界设备中的流都是低端流

Java基础09 类数据与类方法(转载)

我们一直是为了产生对象而定义类(class)的.对象是具有功能的实体,而类是对象的类型分类.这是面向对象的一个基本概念. static数据成员 有一些数据用于表述类的状态.比如Human类,我们可以用“人口”来表示Human类的对象的总数.“人口”直接描述类的状态,而不是某个对象. 类的所有对象共享“人口”数据.这样的数据被称为类数据成员(class field). 在类定义中,我们利用static关键字,来声明类数据成员,比如: class Human{    public Human(int

Java基础03 构造器与方法重载(转载)

显式初始化要求我们在写程序时就确定初始值,这有时很不方便.我们可以使用构造器(constructor)来初始化对象.构造器可以初始化数据成员,还可以规定特定的操作.这些操作会在创建对象时自动执行. 定义构造器 构造器(constructor)是一个方法.像普通方法一样,我们在类中定义构造器.构造器有如下基本特征: 构造器的名字和类的名字相同 构造器没有返回值 我们定义Human类的构造器: public class Test{    public static void main(String[

Java基础02 方法与数据成员(转载)

对象中的数据成员表示对象的状态.对象可以执行方法,表示特定的动作. 此外,我们还了解了类(class).同一类的对象属于相同的类型(type).我们可以定义类,并使用该定义来产生对象. 调用同一对象的数据成员 方法可以调用该对象的数据成员.比如下面我们给Human类增加一个getHeight()的方法.该方法返回height数据成员的值: public class Test{    public static void main(String[] args){        Human aPer