classloader從1.6到1.7總體分紅了兩個版本。重點區別就是並行類加載。java
protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { …… return c; }
1.6版本加了一個方法鎖。redis
private final ConcurrentHashMap parallelLockMap; protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { …… return c; } } protected Object getClassLoadingLock(String className) { Object lock = this; if (parallelLockMap != null) { Object newLock = new Object(); lock = parallelLockMap.putIfAbsent(className, newLock); if (lock == null) { lock = newLock; } } return lock; }
1.7以後就是用了兩種模式getClassLoadingLock方法中,咱們能夠看出有兩種模式,一種是parallelLockMap爲空,鎖的對象是classloader自己,另一種是parallelLockMap不爲null。這裏會根據classname去獲取鎖,若是有返回的object不爲null。說明已經有class使用過了,若是爲null,就把新建的object當作鎖,達到了一個classname一個鎖的效果。算法
有不少classloader的例子直接複寫了loadClass可是沒有加鎖,只有讀取文件加載的過程,這種classloader都是特定場合使用的。並不具有通用性。衆所周知的一個規則,一個classloader不能加載相同類的字節碼,第二次加載就會在defineclass的時候報錯。
場景1
不一樣的線程能夠是相同的classloader,兩個線程的都是A classloader加載的,當裏面的有個方法裏都有B類時,兩個線程都會觸發A加載B類
場景2
雙親委託的狀況,A loader是B loader的parent。A能加載到C類。B loader加載c的時候委託給A,A也在同時加載C。此時觸發了兩次A加載C。數據庫
鎖分離加快了併發,這個是顯而易見的。還有一個好處是減小了死鎖。在編寫javaagent的時候,只要在transform加點鎖,特別容易和classloader還有類初始化時候的鎖形成死鎖。基本死鎖的場景都在1.6。數組
jdk中鎖分離的實現特別多緩存
雙鎖讀寫分離
LinkedBlockingQueue有兩把鎖takeLock和putLock。 因爲隊列的特性——FIFO。在寫入的時候,競爭takeLock。讀取的時候競爭putLock。以此達到同事讀寫的增長吞吐量的目的。
副本機制讀寫分離
副本機制讀寫分離的典型就是copyonwrite。 CopyOnWriteArrayList。只有一把lock。可是使用的是數組存儲。用volatile修飾。寫入的時候獲取鎖,先用一個新的數組把舊的數據拷貝過來,而後把要加入的數據放入新數組中。最後替換 volatile的引用。讀取的時候就直接獲取volatile的數組。 這樣讀取的時候只是那一刻的副本,一旦開始遍歷,直到結束都不會有新的數據加入了。併發
寫寫分離的場景是鎖細化。classloader的改進算是寫寫分離的狀況。典型的場景就是ConcurrentHashMap。ConcurrentHashMap的每一個槽位一把鎖,當沒有hash衝突的時候,元素的寫的過程是並行的。this
集羣的鎖分離的場景咱們也用到的特別多。主要目的是提供併發。線程