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
集群的锁分离的场景咱们也用到的特别多。主要目的是提供并发。线程