Java在一開始就提供了synchronized關鍵字,用於多線程之間的同步。它使用簡便,不會出現拿鎖之后不歸還的情況,可以避免一些編程錯誤。
而jdk5時提供的concurrent包里,有一個Lock接口以及它的實現類:ReentrantLock。這個類提供了更靈活的控制以及更強大的功能。
如果單從性能方面考慮,兩個哪個更高效呢?
首先是單線程的加鎖情況,見以下代碼:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class SynLockTest {
public static void main(String[] args) {
long value = 0;
int MAX = 10000000;
Lock lock = new ReentrantLock();
long start = System.nanoTime();
for (int i = 0; i < MAX; i++) {
synchronized (new Object()) {
value = value + 1;
}
}
long end = System.nanoTime();
System.out.println("synchronized cost: " + (end – start)/1000000 + "ms");start = System.nanoTime();
for (int i = 0; i < MAX; i++) {
lock.lock();
try {
value = value + 1;
} finally {
lock.unlock();
}
}
end = System.nanoTime();
System.out.println("lock cost: " + (end – start) + "ns");
}
}
結果如下:
synchronized cost: 405ms
lock cost: 479ms
可見Lock的運行時間比synchronized略大。可以推測java編譯器為synchronized做了特別優化。
再考慮多線程情況:
public class SynLockTest {
static class SynRunner implements Runnable {
private long v = 0;@Override
public synchronized void run() {
v = v + 1;
}
}static class LockRunner implements Runnable {
private ReentrantLock lock = new ReentrantLock();
private long v = 0;@Override
public void run() {
lock.lock();
try {
v = v + 1;
} finally {
lock.unlock();
}
}}
static class Tester {
private AtomicLong runCount = new AtomicLong(0);
private AtomicLong start = new AtomicLong();
private AtomicLong end = new AtomicLong();public Tester(final Runnable runner, int threadCount) {
final ExecutorService pool = Executors.newFixedThreadPool(threadCount);
Runnable task = new Runnable() {
@Override
public void run() {
while (true) {
runner.run();
long count = runCount.incrementAndGet();
if (count == 1) {
start.set(System.nanoTime());
} else if (count >= 10000000) {
if (count == 10000000) {
end.set(System.nanoTime());
System.out.println(runner.getClass().getSimpleName() + ", cost: "
+ (end.longValue() – start.longValue())/1000000 + "ms"); }
pool.shutdown();
return;
}
}
}
};
for (int i = 0; i < threadCount; i++) {
pool.submit(task);
}
}
}public static void main(String[] args) {
new Tester(new SynRunner(), 1);
new Tester(new LockRunner(), 1);
}}
現在測試不同線程下的表現(時間單位ms):
1 | 10 | 50 | 100 | 500 | 1000 | 5000 | |
synchronized | 542 | 4894 | 4667 | 4700 | 5151 | 5156 | 5178 |
lock | 838 | 1211 | 821 | 847 | 851 | 1211 | 1241 |
可以看到,在多線程環境並存在大量競爭的情況下,synchronized的用時迅速上升,而lock卻依然保存不變或增加很少。
Lock是用CAS來實現的
JDK 1.6以上synchronized也改用CAS來實現了,所以兩者性能差不多
Lock提供的功能豐富點,synchronized的使用簡單點