1.關於兩者的實現的比較
A).一般認爲synchronized關鍵字的實現是源自於像信號量之類的線程同步機制,涉及到線程運行狀態的切換,在高併發狀態下,CPU消耗過多的時間在線程的調度上,從而造成了性能的極大浪費。然而真的如此麼?
線程的狀態主要有一下五種,分別是新建狀態,就緒狀態,運行狀態,阻塞狀態,消亡狀態等5種狀態
B).lock實現原理則是依賴於硬件,現代處理器都支持CAS指令,所謂CAS指令簡單的來說Compare And Set,CPU循環執行指令直到得到所期望的結果,換句話來說就是當變量真實值不等於當前線程調用時的值的時候(說明其他線程已經將這個值改變),就不會賦予變量新的值。這樣就保證了變量在多線程環境下的安全性。
然而,現實情況是當JDK版本高於1.6的時候,synchronized已經被做了CAS的優化:具體是這樣的,當執行到synchronized代碼塊時,先對對象頭的鎖標誌位用lock cmpxchg的方式設置成“鎖住“狀態,釋放鎖時,在用lock cmpxchg的方式修改對象頭的鎖標誌位爲”釋放“狀態,寫操作都立刻寫回主內存。JVM會進一步對synchronized時CAS失敗的那些線程進行阻塞操作(調用操作系統的信號量)(此段來摘自別處)。也就是先CAS操作,不行的話繼而阻塞線程。
除此之外,系統環境,CPU架構,虛擬機環境都會影響兩者的性能關係。
舉例如下
1).X86_64 cpu i7 4910mq @4.0ghz ,Windows10 64bit,JDK1.8 hotspot 64bit虛擬機環境
測試代碼
測試對某Map對象高併發下的讀寫線程安全測試
測試對比有synchronized,ReadWriteLock,ConcurrentHashMap,
public class MapTest {
private Map<Integer,String> map = new ConcurrentHashMap<>();
private long starttime;
private AtomicInteger count = new AtomicInteger(t_count);
private final static int t_count = 5000;
private final static int rw_count = 10000;
Runnable readrun = new Runnable() {
@Override
public void run() {
int i = rw_count;
while (i > 0){
map.get(i);
i--;
}
System.out.println("read-mapsize="+map.size());
if(count.decrementAndGet() == 0)
System.out.println("time="+ (System.currentTimeMillis() - starttime +"ms"));
}
};
Runnable writerun = new Runnable() {
@Override
public void run() {
int i = rw_count;
while (i > 0){
map.put(i,i+"");
i--;
}
System.out.println("write-mapsize="+map.size());
if(count.decrementAndGet() == 0)
System.out.println("time="+ (System.currentTimeMillis() - starttime + "ms"));
}
};
public void run(){
starttime = System.currentTimeMillis();
for(int i = 0;i < t_count/2;i ++){
new Thread(writerun).start();
new Thread(readrun).start();
}
}
}
HashMap 用synchronized重寫
public class SyncHashMap extends HashMap{
@Override
public Object get(Object key) {
// TODO Auto-generated method stub
synchronized (this) {
return super.get(key);
}
}
@Override
public synchronized Object put(Object key, Object value) {
// TODO Auto-generated method stub
synchronized (this) {
return super.put(key, value);
}
}
}
用讀寫鎖實現的Map代理類,有些粗糙,沒加try finally
public class SyncMapProxy<K,V> implements Map<K,V>{
private Map<K,V> origin;
private ReadWriteLock lock;
public SyncMapProxy(Map<K, V> origin) {
this.origin = origin;
lock = new ReentrantReadWriteLock();
}
public static <K,V> SyncMapProxy<K,V> SyncMap(Map<K,V> map){
return new SyncMapProxy<K,V>(map);
}
@Override
public void clear() {
lock.writeLock().lock();
origin.clear();
lock.writeLock().unlock();
}
@Override
public boolean containsKey(Object key) {
lock.readLock().lock();
boolean res = origin.containsKey(key);
lock.readLock().unlock();
return res;
}
@Override
public boolean containsValue(Object value) {
lock.readLock().lock();
boolean res = origin.containsKey(value);
lock.readLock().unlock();
return res;
}
@Override
public Set<Entry<K, V>> entrySet() {
lock.readLock().lock();
Set<Entry<K, V>> res = origin.entrySet();
lock.readLock().unlock();
return res;
}
@Override
public V get(Object key) {
lock.readLock().lock();
V res = origin.get(key);
lock.readLock().unlock();
return res;
}
@Override
public boolean isEmpty() {
return origin.isEmpty();
}
@Override
public Set<K> keySet() {
lock.readLock().lock();
Set<K> res = origin.keySet();
lock.readLock().unlock();
return res;
}
@Override
public V put(K key, V value) {
lock.writeLock().lock();
V v = origin.put(key, value);
lock.writeLock().unlock();
return v;
}
@Override
public void putAll(Map<? extends K, ? extends V> map) {
lock.writeLock().lock();
origin.putAll(map);
lock.writeLock().unlock();
}
@Override
public V remove(Object key) {
lock.writeLock().lock();
V v = origin.remove(key);
lock.writeLock().unlock();
return v;
}
@Override
public int size() {
return origin.size();
}
@Override
public Collection<V> values() {
lock.readLock().lock();
Collection<V> res = origin.values();
lock.readLock().unlock();
return res;
}
}