首页 > 编程语言 >【设计模式】策略模式——策略模式在JDK源码中的应用

【设计模式】策略模式——策略模式在JDK源码中的应用

时间:2024-01-14 19:31:35浏览次数:30  
标签:return 模式 源码 c2 c1 设计模式 RejectedExecutionHandler public ThreadPoolExecutor

策略模式在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排序策略,这是典型的策略模式。



标签:return,模式,源码,c2,c1,设计模式,RejectedExecutionHandler,public,ThreadPoolExecutor
From: https://blog.51cto.com/dongfeng9ge/9241654

相关文章

  • python面向对象之单例模式的使用
    单例模式​ 单例模式(SingletonPattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。​ 比如,某个程序的配置信息存放在一个文件中,客户端通过一个Appconfig的类来读取配置......
  • spring与设计模式之四适配器模式
    一、定义适配器模式-或者称为转接口模式,变压器模式。通过适配,可以让原来提供特定功能的对象完成另外一个标准的功能。所以,所谓的适配应该可以这样称呼:让某些类/接口适配/转换某个标准/功能。适配器器的重点是适配,就是新增(装饰)。为了便于记忆和理解,读者最好根据自己的习惯来命......
  • 建造者模式
    定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示,用户只需要指定需要建造的类型就可以得到它们,建造过程及细节不需要知道使用场景:如果一个对象有非常复杂的数据结构(很多属性),想把复杂的创建和使用分离优点:封装性好,创建和使用分离扩展性......
  • PyTorch项目源码学习(3)——Module类初步学习
    torch.nn.ModuleModule类是用户使用torch来自定义网络模型的基础,Module的设计要求包括低耦合性,高模块化等等。一般来说,计算图上所有的子图都可以是Module的子类,包括卷积,激活函数,损失函数节点以及相邻节点组成的集合等等,注意这里的关键词是“节点”,Module族类在计算图中主要起到搭......
  • 基于SpringBoot+Vue的OA办公系统设计实现(源码+lw+部署文档+讲解等)
    (文章目录)前言:heartpulse:博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌:heartpulse:......
  • Feign源码解析5:loadbalancer
    背景经过前面几篇的理解,我们大致梳理清楚了FeignClient的创建、Feign调用的大体流程,本篇会深入Feign调用中涉及的另一个重要组件:loadbalancer,了解loadbalancer在feign调用中的职责,再追溯其是如何创建的。在讲之前,我先提个重点,本文章的前期是引用了nacos依赖且开启了如下选项,启用......
  • Golang流水线设计模式实践
    Golang流水线设计模式实践原创 俞凡DeepNoMind DeepNoMind 2024-01-1411:45 发表于上海 听全文流水线设计模式对于顺序处理业务数据非常有用,可以以一致的方式直观的定义对数据的处理流程。原文: UsingaPipelinePatterninGolang[1]到目前为止,我已经将Gola......
  • 性能篇:深入源码解析和性能测试arraylist和LinkedList差异!
    嗨,大家好,我是小米!今天我们要谈论的是Java中两个常用的集合类:ArrayList和LinkedList。大家都知道,这两者在新增和删除元素的操作上有一些差异,那么它们究竟在性能上有何表现呢?我们通过深入源码解析和性能测试来一探究竟!ArrayList新增元素到末尾这是最常见的新增元素操作,我们使用......
  • 【软件开发学习笔记】设计模式
    【软件开发学习笔记】设计模式设计模式设计模式是一种针对面向对象语言的软件设计方法,是对类设计的约束和指导。设计模式由“原则”和“方法”两部分组成,一个设计良好的项目结构应能完美符合“原则”中的要求,而为了实现完美往往需要按照“方法”的指导去设计。原则:这是必须......
  • 深入理解 Hadoop (一)网络通信架构与源码浅析
    HadoopRPC网络通信框架原理剖析YARNRPC服务端的工作大致可以分为四个阶段:第一个阶段:Server初始化和启动在Server初始化的时候,会初始化Listener组件(内部启动了一个AcceptSelector绑定了相应的端口,用来处理客户端的OP_ACCEPT事件),内部还初始化了一组Reader线程,其......