JDK 6.0
JDK 6.0对锁做了一些优化,比如锁自旋、锁消除、锁合并、轻量级锁、所偏向等。在这里不一一介绍,但是给一个例子以有感性认识:
import java.util.Vector;
public class LockElimination {
public String getStr() {
Vector v = new Vector();
v.add(3);
v.add(4);
return v.toString();
}
public static void main(String[] args) {
System.out.println(new LockElimination().getStr());
}
}
在这个例子中,对vector的加锁完全是没有必要的,这样的锁是可以被优化消除的。
CyclicBarrier是JDK 6.0新增的一个用于流程控制的类,这个类可以保证多个任务在并行执行都完成的情况下,再统一执行下一步操作:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class BarrierUsage extends Thread {
private static CyclicBarrier barrier = new CyclicBarrier(2, new Thread() {
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println("finish");
};
});
private final int sleepMilSecs;
public BarrierUsage(int sleepMilSecs) {
this.sleepMilSecs = sleepMilSecs;
}
@Override
public void run() {
try {
Thread.sleep(sleepMilSecs);
System.out.println(sleepMilSecs + " secs slept");
barrier.await();
} catch (InterruptedException e) {
} catch (BrokenBarrierException e) {
}
}
public static void main(String[] args) {
new BarrierUsage(2000).start();
new BarrierUsage(4000).start();
}
}
上面这个例子就模拟了,两个子任务(分别执行2000毫秒和4000毫秒)完成以后,再执行一个总任务(2000毫秒)并打印完成。
还有一个类似的类是CountDownLatch(使用倒数计数的方式),这样的类出现标志着,JDK对并发的设计已经逐步由微观转向宏观了,开始逐步重视并发程序流程,甚至是框架上的设计,这样的思路我们会在下文的JDK 7.0中继续看到。
JDK 7.0
2011年的JDK 7.0进一步完善了并发流程控制的功能,比如fork-join框架:
把任务分解成不同子任务完成;比如Phaser这个类,整合了CyclicBarrier和CountDownLatch两个类的功能,但是提供了动态修改依赖目标的能力;还有NIO2的新开放特性。这里不详细介绍了。
Java的未来
在多线程编程方面,Java的未来会怎样?
JDK 8.0按计划将在2013年夏天发布,Java从动态语言那里学了很多过来,比如闭包等等,在多线程方面会怎样呢?郁于JLS所限,无法有大的突破,还是有另辟蹊径的办法?纵观整个Java发展的历程,都在努力修正多线程模型实现上的种种弊端,尽可能在保留虚拟机优化特性的基础上给使用者屏蔽细节。
在来回想一下Java最基础的线程模型,其他语言是怎样实现的呢?
比如C#,任何类的任何方法,都可以成为线程的执行方法:
using System;
using System.Threading;
public class AnyClass {
public void DoSth() {
Console.WriteLine("working");
}
}
class ThreadTest{
public static void Main() {
AnyClass anyClass = new AnyClass();
ThreadStart threadDelegate = new ThreadStart(anyClass.DoSth);
Thread myThread = new Thread(threadDelegate);
myThread.Start();
}
}
上面的AnyClass的DoSth方法,就模拟线程执行打印了一句话。
再来看一门小众语言Io,在语法糖的帮助下,实现更加简单:
thread := Object clone
thread start := method("working" println)
thread @@start
因为Io是基于原型的语言(如果你有兴趣的话,可以在我的blog里找到Io介绍),通过这样的@符号,就实现了新启一个线程运行的功能。
再来看看JDK 5.0的ReentrantLock类,它完全实现了synchronized语义上的全部功能,并且还能具备诸如条件锁、锁超时、公平锁等等更优越的特性(特别值得一提的是tryLock的功能也实现了,就是说可以判定假如这个时间获取锁是否能够成功),甚至在并发量居高不下时,性能还更加优越……我不禁要问,用一个Java实现的锁类去从功能上代替一个已有的同步关键字,这岂不是Java自己在抽自己嘴巴?
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockUsage implements Runnable {
private static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
lock.lock();
try {
System.out.println("do something 1");
Thread.sleep(2000);
} catch (InterruptedExc