Java性能调优实战
刘超
金山软件西山居技术经理
立即订阅
7535 人已学习
课程目录
已完结 48 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 怎样才能做好性能调优?
免费
模块一 · 概述 (2讲)
01 | 如何制定性能调优标准?
02 | 如何制定性能调优策略?
模块二 · Java编程性能调优 (10讲)
03 | 字符串性能优化不容小觑,百M内存轻松存储几十G数据
04 | 慎重使用正则表达式
05 | ArrayList还是LinkedList?使用不当性能差千倍
加餐 | 推荐几款常用的性能测试工具
06 | Stream如何提高遍历集合效率?
07 | 深入浅出HashMap的设计与优化
08 | 网络通信优化之I/O模型:如何解决高并发下I/O瓶颈?
09 | 网络通信优化之序列化:避免使用Java序列化
10 | 网络通信优化之通信协议:如何优化RPC网络通信?
11 | 答疑课堂:深入了解NIO的优化实现原理
模块三 · 多线程性能调优 (10讲)
12 | 多线程之锁优化(上):深入了解Synchronized同步锁的优化方法
13 | 多线程之锁优化(中):深入了解Lock同步锁的优化方法
14 | 多线程之锁优化(下):使用乐观锁优化并行操作
15 | 多线程调优(上):哪些操作导致了上下文切换?
16 | 多线程调优(下):如何优化多线程上下文切换?
17 | 并发容器的使用:识别不同场景下最优容器
18 | 如何设置线程池大小?
19 | 如何用协程来优化多线程业务?
20 | 答疑课堂:模块三热点问题解答
加餐 | 什么是数据的强、弱一致性?
模块四 · JVM性能监测及调优 (6讲)
21 | 磨刀不误砍柴工:欲知JVM调优先了解JVM内存模型
22 | 深入JVM即时编译器JIT,优化Java编译
23 | 如何优化垃圾回收机制?
24 | 如何优化JVM内存分配?
25 | 内存持续上升,我该如何排查问题?
26 | 答疑课堂:模块四热点问题解答
模块五 · 设计模式调优 (6讲)
27 | 单例模式:如何创建单一对象优化系统性能?
28 | 原型模式与享元模式:提升系统性能的利器
29 | 如何使用设计模式优化并发编程?
30 | 生产者消费者模式:电商库存设计优化
31 | 装饰器模式:如何优化电商系统中复杂的商品价格策略?
32 | 答疑课堂:模块五思考题集锦
模块六 · 数据库性能调优 (8讲)
33 | MySQL调优之SQL语句:如何写出高性能SQL语句?
34 | MySQL调优之事务:高并发场景下的数据库事务调优
35 | MySQL调优之索引:索引的失效与优化
36 | 记一次线上SQL死锁事故:如何避免死锁?
37 | 什么时候需要分表分库?
38 | 电商系统表设计优化案例分析
39 | 数据库参数设置优化,失之毫厘差之千里
40 | 答疑课堂:MySQL中InnoDB的知识点串讲
模块七 · 实战演练场 (4讲)
41 | 如何设计更优的分布式锁?
42 | 电商系统的分布式事务调优
43 | 如何使用缓存优化系统性能?
44 | 记一次双十一抢购性能瓶颈调优
结束语 (1讲)
结束语 | 栉风沐雨,砥砺前行!
Java性能调优实战
登录|注册

28 | 原型模式与享元模式:提升系统性能的利器

刘超 2019-07-25
你好,我是刘超。
原型模式和享元模式,前者是在创建多个实例时,对创建过程的性能进行调优;后者是用减少创建实例的方式,来调优系统性能。这么看,你会不会觉得两个模式有点相互矛盾呢?
其实不然,它们的使用是分场景的。在有些场景下,我们需要重复创建多个实例,例如在循环体中赋值一个对象,此时我们就可以采用原型模式来优化对象的创建过程;而在有些场景下,我们则可以避免重复创建多个实例,在内存中共享对象就好了。
今天我们就来看看这两种模式的适用场景,了解了这些你就可以更高效地使用它们提升系统性能了。

原型模式

我们先来了解下原型模式的实现。原型模式是通过给出一个原型对象来指明所创建的对象的类型,然后使用自身实现的克隆接口来复制这个原型对象,该模式就是用这种方式来创建出更多同类型的对象。
使用这种方式创建新的对象的话,就无需再通过 new 实例化来创建对象了。这是因为 Object 类的 clone 方法是一个本地方法,它可以直接操作内存中的二进制流,所以性能相对 new 实例化来说,更佳。

实现原型模式

我们现在通过一个简单的例子来实现一个原型模式:
//实现Cloneable 接口的原型抽象类Prototype
class Prototype implements Cloneable {
//重写clone方法
public Prototype clone(){
Prototype prototype = null;
try{
prototype = (Prototype)super.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return prototype;
}
}
//实现原型类
class ConcretePrototype extends Prototype{
public void show(){
System.out.println("原型模式实现类");
}
}
public class Client {
public static void main(String[] args){
ConcretePrototype cp = new ConcretePrototype();
for(int i=0; i< 10; i++){
ConcretePrototype clonecp = (ConcretePrototype)cp.clone();
clonecp.show();
}
}
}
要实现一个原型类,需要具备三个条件:
实现 Cloneable 接口:Cloneable 接口与序列化接口的作用类似,它只是告诉虚拟机可以安全地在实现了这个接口的类上使用 clone 方法。在 JVM 中,只有实现了 Cloneable 接口的类才可以被拷贝,否则会抛出 CloneNotSupportedException 异常。
重写 Object 类中的 clone 方法:在 Java 中,所有类的父类都是 Object 类,而 Object 类中有一个 clone 方法,作用是返回对象的一个拷贝。
在重写的 clone 方法中调用 super.clone():默认情况下,类不具备复制对象的能力,需要调用 super.clone() 来实现。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Java性能调优实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(24)

  • 知行合一
    new一个对象和clone一个对象,性能差在哪里呢?文中提到直接从内存复制二进制这里不是很理解

    作者回复: 一个对象通过new创建的过程为:
    1、在内存中开辟一块空间;
    2、在开辟的内存空间中创建对象;
    3、调用对象的构造函数进行初始化对象。

    而一个对象通过clone创建的过程为:
    1、根据原对象内存大小开辟一块内存空间;
    2、复制已有对象,克隆对象中所有属性值。

    相对new来说,clone少了调用构造函数。如果构造函数中存在大量属性初始化或大对象,则使用clone的复制对象的方式性能会好一些。

    2019-07-26
    18
  • 木木匠
    单例模式是针对某个类的单例,享元模式可以针对一个类的不同表现形式的单例,享元模式是单例模式的超集。

    作者回复: 言简意赅!

    2019-07-25
    14
  • QQ怪
    享元模式可以再次创建对象 也可以取缓存对象

    单例模式则是严格控制单个进程中只有一个实例对象

    享元模式可以通过自己实现对外部的单例 也可以在需要的使用创建更多的对象

    单例模式是自身控制 需要增加不属于该对象本身的逻辑

    作者回复: 理解很透彻,点赞

    2019-07-25
    7
  • 东方奇骥
    老师,请教一下,文中说的,@service默认是单例模式,若不想影响下次请求,就要使用原型模式。能举个例子吗,什么时候会影响下次请求,不是很理解,因为我的项目里基本都是单例模式

    作者回复: 这里纠正下,不是每次请求,而是每次bean注入或通过上下文获取bean时。

    如果我们使用的是单例,假设有一个全局变量private int a=1,我们通过上下文获取到实例,调用A方法修改了变量a=2,此时下一个通过上下文获取到实例调用B方法获取变量,则a=2。

    如果我们使用的是原型模式,假设有一个全局变量private int a=1,我们通过上下文获取到实例,调用A方法修改了变量a=2,此时下一个通过上下文获取到实例调用B方法获取变量,则还是a=1。

    2019-07-27
    5
  • 公号-代码荣耀
    享元模式的实例也需要考虑线程安全哇?

    作者回复: 需要的。共享数据尽量不要涉及到线程安全问题,否则就没有什么优势了。例如字符串则利用了不可变性来避免线程安全问题。

    2019-07-28
    3
  • Liam
    老师好,文中举例Spring的prototype貌似不是原型模式的实现吧,每次spring都是通过反射创建的对象,并没有通过clone的方式吧

    作者回复: 是的,通过反射创建对象的

    2019-07-26
    3
  • kyle
    个人理解是这样,单例模式,就是单个实例的模式,一般针对类来说的,也就是类实例,一个单例模式包含一个类实例。而享元模式,就是共享元对象的模式,元对象除了可以是类实例,也可以是其他可以共享的对象,比如普通类型变量,字符常量等。一个享元模式可以包含多个元对象。简单说就是,享元模式是单例模式的超集,单例模式是享元模式的子集。

    作者回复: 对的

    2019-10-11
    1
  • Aaron
    老师请教你个问题,线上短信业务被轰炸,流量费倍增……求推荐个解决思路,监测发现是爬虫程序

    作者回复: 建议加一个图片验证码

    2019-07-26
    1
    1
  • 全有
    享元模式的给工厂类,是用HashMap 来存储共享对象,在多线程下并不安全,同时也没有加锁判定,依然会存在创建个对象,只是会覆盖掉
    2019-07-25
    1
  • 玉骢哒哒
    private static void test(){
            long startTime = System.currentTimeMillis();
            for(int i=0;i<100000000;i++){
                Student student = new Student();
            }
            long endTime = System.currentTimeMillis();
            System.out.println("startTime="+startTime+",endTime="+endTime+",差值"+(endTime-startTime));
        }
        private static void test2(){
            long startTime = System.currentTimeMillis();
            Student student = new Student();
            for(int i=0;i<100000000;i++){
                Student stu = (Student)student.clone();
            }
            long endTime = System.currentTimeMillis();
            System.out.println("startTime="+startTime+",endTime="+endTime+",差值"+(endTime-startTime));
        }
    为什么使用clone反而耗时增加了很多
    2019-11-04
    1
  • Alsace
    如果在程序中利用一个map容器缓存对象,实现懒加载,存在的话直接取,不存在生成新对象维护到容器中,是不是也算是享元模式呢?

    作者回复: 是的

    2019-10-24
  • Demon.Lee
    ---1000万
    22:50:16.799 [main] INFO com.learn.geektime.javafamily.performance.ch28.PrototypeTest - original way cost: 16100, stuList.size: 10000000
    22:51:08.762 [main] INFO com.learn.geektime.javafamily.performance.ch28.PrototypeTest - prototype way cost: 16319, stuList2.size: 10000000

    ---100万
    22:54:08.289 [main] INFO com.learn.geektime.javafamily.performance.ch28.PrototypeTest - original way cost: 195, stuList.size: 1000000
    22:53:31.767 [main] INFO com.learn.geektime.javafamily.performance.ch28.PrototypeTest - prototype way cost: 199, stuList2.size: 1000000

    ---10万
    22:54:49.290 [main] INFO com.learn.geektime.javafamily.performance.ch28.PrototypeTest - original way cost: 39, stuList.size: 100000
    22:55:03.003 [main] INFO com.learn.geektime.javafamily.performance.ch28.PrototypeTest - prototype way cost: 58, stuList2.size: 100000

    ---我猜测原型模式不适合这种简单的pojo,估计适合大对象
    2019-09-14
  • godtrue
    课后思考及问题
    1:原型模式——通过使用更高效的对象创建方式来创建大量重复对象,已提高创建对象的性能。
    2:享员模式——通过减少内部公享数据的创建来创建对象,以提高创建对象的性能。
    3:单列模式我觉得可以认为是一个缩小版的享员模式,它的所有数据都可以看着共享的数据,都无需重复创建。
    请问老师,我们可以使用原型模式,通过 clone 方法复制对象,这种方式比用 new 和序列化创建对象的效率要高。
    这个结论有定量的测试数据嘛?性能大概能提升多少?如果性能提高不少,我觉得可以作为我们系统的一个性能优化点,我们复制对象采用深拷贝的方式,担心序列化反序列化性能差都是new后重新赋值的方式实现的,对于对象属性是层层递归创建对象赋值实现的。
    或者换个问题老师觉得最佳的对象深拷贝方案是什么?为啥?
    2019-09-12
  • 疯狂咸鱼
    代码是不是写错了应该是:
    学生 1:test2
    学生 2:test2

    作者回复: 是的,感谢提醒

    2019-09-01
  • 业余草
    变一下不就是工厂模式吗?
    2019-08-01
  • Aaron
    谢谢🙏老师提供的思路🙏
    2019-07-27
  • 门窗小二
    通过老师这次的讲解算是彻底明白了单例模式与享元模式的区别,享元模式可以理解为一组单例模式

    作者回复: 对的

    2019-07-25
  • 程序员人生
    单例模式的运用场景一般是一个系统中的全局事物,比如数据库连接池,多线程连接池等
    享元模式的运用场景则是是业务流程中,需要频繁获取元数据的的情况,比如老师说的用户获取商品的情况。
    2019-07-25
  • Jxin
    实现一个公共父类,实现原型模式,并反射完成深拷贝。对需要大量创建新对象的类继承该父类。老师这样做行不?反射有开销,继承这种结构也不好(但组合实现感觉不直观)。不确定这样抽象后是否利大于弊。毕竟如果反射开销冲掉了clone带来的性能优化,还不如直接new

    作者回复: 想法很好,不建议这样曲线救国。我们没有必要针对每一个类去做原型设计,仅仅针对一些特殊场景的类实现clone方法即可。

    2019-07-25
  • 我已经设置了昵称
    享元模式和策略模式感觉有点像啊,根据某个值,去上下文容器中取对应的handler类处理
    2019-07-25
收起评论
24
返回
顶部