总是套路留人心, JAVA提供的套路: LinkedHashMap实现LRU缓存; InvocationHandler实现动态代理; fork/join实现窃取算法
1. LinkedHashMap实现LRU缓存
LRU缓存核心是根据访问顺序排序, 自动移除队尾缓存, LinkedHashMap已经实现了这些要求:
public LRUCache<K, V> extends LinkedHashMap<K, V> { private int cacheSize; public LRUCache(int cacheSize){ super(16, 0.75, true); // key1: true表示使用访问排序, 默认false表示插入排序 } @Override protected boolean removeEldestEntry(Map.Entry<k, V> eldest){ return size() >= cacheSize; //key2: 每次调用add后, 底层都会调用这个方法判断是否执行移除队尾元素, 通过重写这个方法来控制移除时机 } }
2.InvocationHandler实现动态代理
手工为每一个类实现一个代理类称为静态代理, 缺点是即使代理的操作是一样的, 仍然要为每个类实现一个代理类, 代理操作代码不能复用
//1.实现代理类 public class MyInvocationHandler implements InvocationHandler { private Object target; MyInvocationHandler() { super(); } MyInvocationHandler(Object target) { super(); this.target = target; } @Override public Object invoke(Object o, Method method, Object[] args) throws Throwable { if("getName".equals(method.getName())){ System.out.println("++++++before " + method.getName() + "++++++"); Object result = method.invoke(target, args); System.out.println("++++++after " + method.getName() + "++++++"); return result; }else{ Object result = method.invoke(target, args); return result; } } //2.获得代理对象 (TargetInterface)Proxy.newProxyInstance(tarInterClass.getClassLoader(), tarInterClass.getInterface(), new MyInvocationHandler(tarObj));
可以看出Proxy.newProxyInstance()获得的是对一个接口的代理, 这也是InvocationHandler动态代理的缺点:只能代理接口中的方法.
想代理类中非implement接口的方法, 可以用第三方Cglib代理实现
3.fork/join实现窃取模式
窃取模式是一种升级版的生产者-消费者模式, 特点是线程在处理完自己的任务队列后, 回去其他线程的任务队列的末尾窃取任务来执行, 以提高效率.
如果自己实现窃取模式:首先要一个线程池来执行任务, 其次要为每一个线程提供一个任务队列, 最后还要实现窃取其他队列的任务的功能. 幸运的是JAVA已经提供了实现.
//1.编写任务类implement RecursiveAction 不反回结果, 相当于Runnable RecursiveTask 返回结果, 相当于Callable //2.获得线程池 ForkJoinPool pool = new ForkJoinPool() //3.提交任务 pool.execute() 异步执行命令 pool.invoke() 执行命令并直接返回结果 pool.submit() 执行任务并返回一个ForkJoinTask(Future) //4.子RecusiveAction fork() //把自身添加到任务执行队列 join() //获得执行结果
在compute()的实现中, 根据条件将任务分成更细的多个RecusiveTask, 就实现了分治法