策略模式在JDK中具有广泛的应用,本文只讨论最常见的应用。
RejectedExecutionHandler
在线程池使用有界队列并且最大线程数不为Integer.MAX_VALUE的时候,一旦task数量达到临界点,新的task添加到线程池的时候就会出现问题,ThreadPoolExecutor的构造方法中参数最多的方法中最后一个参数就是为了处理这些问题的,RejectedExecutionHandler的源码如下:
public interface RejectedExecutionHandler {
/**
* 当超出了最大线程数或阻塞队列容量时,
* 或者当ThreadPoolExecutor关闭时,
* 导致ThreadPoolExecutor无法接收task时调用该方法
*/
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
很明显ThreadPoolExecutor本身扮演上下文角色,RejectedExecutionHandler扮演抽象策略角色,而实现RejectedExecutionHandler接口的几个子类扮演具体策略角色:
/**
* 由持有ThreadPoolExecutor的线程立即执行该task
*/
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
/**
* 丢弃task并抛出RejectedExecutionException异常
*/
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
/**
* 丢弃task但不抛出异常
*/
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
/**
* 丢弃阻塞队列中最前边的task,并处理当前的task
*/
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
Comparator
Comparator也是策略模式在JDK源码里一个比较常用的应用场景,最常见于算法机考的排序问题。Comparator的代码如下:
public interface Comparator<T> {
int compare(T o1, T o2);
// ……代码省略……
}
比较典型的一个应用场景就是CookieManager给本地保存的cookie排序的策略:
static List<String> sortByPathAndAge(List<HttpCookie> cookies) {
cookies.sort(new CookieComparator());
List<String> cookieHeader = new java.util.ArrayList<>();
for (HttpCookie cookie : cookies) {
if (cookies.indexOf(cookie) == 0 && cookie.getVersion() > 0) {
cookieHeader.add("$Version=\"1\"");
}
cookieHeader.add(cookie.toString());
}
return cookieHeader;
}
static class CookieComparator implements Comparator<HttpCookie> {
public int compare(HttpCookie c1, HttpCookie c2) {
if (c1 == c2) return 0;
if (c1 == null) return -1;
if (c2 == null) return 1;
String p1 = c1.getPath();
String p2 = c2.getPath();
p1 = (p1 == null) ? "" : p1;
p2 = (p2 == null) ? "" : p2;
int len1 = p1.length();
int len2 = p2.length();
if (len1 > len2)
return -1;
if (len2 > len1)
return 1;
// 根据创建时间排序,创建时间较早的排在前边
long creation1 = c1.getCreationTime();
long creation2 = c2.getCreationTime();
if (creation1 < creation2) {
return -1;
}
if (creation1 > creation2) {
return 1;
}
return 0;
}
}
// Android的特殊JDK的排序方式
static class CookiePathComparator implements Comparator<HttpCookie> {
public int compare(HttpCookie c1, HttpCookie c2) {
if (c1 == c2) return 0;
if (c1 == null) return -1;
if (c2 == null) return 1;
// 只有同名的cookie才有排序的意义
if (!c1.getName().equals(c2.getName())) return 0;
// Android-changed: normalize before comparison.
final String c1Path = normalizePath(c1.getPath());
final String c2Path = normalizePath(c2.getPath());
// 同名的cookie,保存路径较短的排在前边
if (c1Path.startsWith(c2Path))
return -1;
else if (c2Path.startsWith(c1Path))
return 1;
else
return 0;
}
}
由此可见普通的SDK与Android的特殊SDK有不同的cookie排序策略,这是典型的策略模式。