转载自 人人都能掌握的Java服务端性能优化方案

作为一个Java后端开发,我们写出的大部分代码都决定着用户的使用体验。如果我们的代码性能不好,那么用户在访问我们的网站时就要浪费一些时间等待服务器的响应。这就可能导致用户投诉甚至用户的流失。

关于性能优化是一个很大的话题。《Java程序性能优化》说性能优化包含五个层次:设计调优、代码调优、JVM调优、数据库调优、操作系统调优等。而每一个层次又包含很多方法论和最佳实践。本文不想大而广的概述这些内容。只是举几个常用的Java代码优化方案,读者看完之后可以真正的实践到自己代码中的方案。

使用单例

对于IO处理、数据库连接、配置文件解析加载等一些非常耗费系统资源的操作,我们必须对这些实例的创建进行限制,或者是始终使用一个公用的实例,以节约系统开销,这种情况下就需要用到单例模式。

public class Singleton {     private volatile static Singleton singleton;     private Singleton (){}     public static Singleton getSingleton() {     if (singleton == null) {         synchronized (Singleton.class) {         if (singleton == null) {             singleton = new Singleton();         }         }     }     return singleton;     }  }

单例模式有很多种写法,我的公众号也推送过多篇和单例相关的文章:

单例模式的七种写法

设计模式(二)——单例模式

设计模式(三)——JDK中的那些单例

不使用synchronized和lock,如何实现一个线程安全的单例?

不使用synchronized和lock,如何实现一个线程安全的单例?(二)

深度解析单例与序列化之间的爱恨情仇~

使用线程池

合理利用线程池能够带来三个好处。

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

在 Java 5 之后,并发编程引入了一堆新的启动、调度和管理线程的API。Executor 框架便是 Java 5 中引入的,其内部使用了线程池机制,它在 java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。

public class MultiThreadTest {  public static void main(String[] args) {      ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("thread-%d").build();      ExecutorService executor = new ThreadPoolExecutor(2, 5, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory);      executor.execute(new Runnable() {          @Override          public void run() {             System.out.println("hello world !");          }      });      System.out.println(" ===> main Thread! " );  }}

使用Future模式

假设一个任务执行起来需要花费一些时间,为了省去不必要的等待时间,可以先获取一个“提货单”,即Future,然后继续处理别的任务,直到“货物”到达,即任务执行完得到结果,此时便可以用“提货单”进行提货,即通过Future对象得到返回值。

public class RealData implements Callable<String> {     protected String data;  

   public RealData(String data) {         this.data = data;     }  

   @Override     public String call() throws Exception {         //利用sleep方法来表示真是业务是非常缓慢的         try {             Thread.sleep(1000);         } catch (InterruptedException e) {             e.printStackTrace();         }         return data;     }  }  

public class Application {     public static void main(String[] args) throws Exception {         FutureTask<String> futureTask =                  new FutureTask<String>(new RealData("name"));         ExecutorService executor =                  Executors.newFixedThreadPool(1); //使用线程池         //执行FutureTask,相当于上例中的client.request("name")发送请求         executor.submit(futureTask);         //这里可以用一个sleep代替对其他业务逻辑的处理         //在处理这些业务逻辑过程中,RealData也正在创建,从而充分了利用等待时间         Thread.sleep(2000);         //使用真实数据         //如果call()没有执行完成依然会等待         System.out.println("数据=" + futureTask.get());     }  }

使用NIO

JDK自1.4起开始提供全新的I/O编程类库,简称NIO,其不但引入了全新高效的Buffer和Channel,同时,还引入了基于Selector的非阻塞 I/O机制,将多个异步的I/O操作集中到一个或几个线程当中进行处理,使用NIO代替阻塞I/O能提高程序的并发吞吐能力,降低系统的开销。

对于每一个请求,如果单独开一个线程进行相应的逻辑处理,当客户端的数据传递并不是一直进行,而是断断续续的,则相应的线程需要 I/O等待,并进行上下文切换。而使用NIO引入的Selector机制后,可以提升程序的并发效率,改善这一状况。

public class NioTest {     static public void main( String args[] ) throws Exception {         FileInputStream fin = new FileInputStream("c:\\test.txt");         // 获取通道         FileChannel fc = fin.getChannel();         // 创建缓冲区         ByteBuffer buffer = ByteBuffer.allocate(1024);         // 读取数据到缓冲区         fc.read(buffer);         buffer.flip();         while (buffer.remaining()>0) {             byte b = buffer.get();             System.out.print(((char)b));         }         fin.close();     }  }

锁优化

在并发场景中,我们的代码中经常会用到锁。存在锁,就必然存在锁的竞争,存在锁的竞争,就会消耗很多资源。那么,如何优化我们Java代码中的锁呢?主要可以从以下几个方面考虑:

  • 减少锁持有时间

    • 可以使用同步代码块来代替同步方法。这样既可以减少锁持有的时间。

  • 减少锁粒度

    • 要在并发场景中使用Map的时候,记得使用ConcurrentHashMap来代替HashTable和HashMap。

  • 锁分离

    • 普通锁(如syncronized)会导致读阻塞写、写也会阻塞读,同时读读与写写之间也会进行阻塞,可以想办法将读操作和写操作分离开。

  • 锁粗化

    • 有些情况下我们希望把很多次锁的请求合并成一个请求,以降低短时间内大量锁请求、同步、释放带来的性能损耗。

  • 锁消除

    • 锁消除是Java虚拟机在JIT编译是,通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁,通过锁消除,可以节省毫无意义的请求锁时间。

关于锁优化的内容,后面会出一篇文章详细介绍。

压缩传输

在进行数据传输之前,可以先将数据进行压缩,以减少网络传输的字节数,提升数据传输的速度,接收端可以将数据进行解压,以还原出传递的数据,并且,经过压缩的数据还可以节约所耗费的存储介质(磁盘或内存)的空间以及网络带宽,降低成本。当然,压缩也并不是没有开销的,数据压缩需要大量的CPU计算,并且,根据压缩算法的不同,计算的复杂度以及数据的压缩比也存在较大差异。一般情况下,需要根据不同的业务场景,选择不同的压缩算法。

缓存结果

对于相同的用户请求,如果每次都重复的查询数据库,重复的进行计算,将浪费很多的时间和资源。将计算后的结果缓存到本地内存,或者是通过分布式缓存来进行结果的缓存,可以节约宝贵的CPU计算资源,减少重复的数据库查询或者是磁盘I/O,将原本磁头的物理转动变成内存的电子运动,提高响应速度,并且线程的迅速释放也使得应用的吞吐能力得到提升。

人人都能掌握的Java服务端性能优化方案相关推荐

  1. 小工匠聊架构-超高并发秒杀系统设计 05_服务端性能优化

    文章目录 Pre 概 影响性能的因素 响应时间 与 QPS 线程数 与 QPS 如何发现瓶颈 工具 JProfiler 和 Yourkit 如何简单的判断 CPU 是不是瓶颈 如何优化系统 减少编码 ...

  2. Flex通信-Java服务端通信实例

    Flex与Java通信的方式有很多种,比较常用的有以下方式: WebService:一种跨语言的在线服务,只要用特定语言写好并部署到服务器,其它语言就可以调用 HttpService:通过http请求 ...

  3. 聊一聊 Java 服务端中的乱象

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:阿里巴巴中间件 查尔斯·狄更斯在<双城记>中写道 ...

  4. 那些年,我们见过的 Java 服务端乱象

    点击上方"方志朋",选择"设为星标" 做积极的人,而不是积极废人 Photo by The Book Tutor @Youtube 文 | 陈昌毅 导读 查尔斯 ...

  5. MobileIMSDK怎样修改服务端核心jar包的源码并替换掉Java服务端的jar包

    场景 MobileIMSDK怎样将Java服务端运行起来以及打成jar包运行: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/11 ...

  6. 那些年,我们见过的 Java 服务端“问题”

    导读 明代著名的心学集大成者王阳明先生在<传习录>中有云: 道无精粗,人之所见有精粗.如这一间房,人初进来,只见一个大规模如此.处久,便柱壁之类,一一看得明白.再久,如柱上有些文藻,细细都 ...

  7. 那些年,我们见过的Java服务端乱象

    导读 查尔斯·狄更斯在<双城记>中写道:"这是一个最好的时代,也是一个最坏的时代."移动互联网的快速发展,出现了许多新机遇,很多创业者伺机而动:随着行业竞争加剧,互联网 ...

  8. Java 服务端乱象大盘点

    作者 | 陈昌毅责编 | 伍杏玲查尔斯·狄更斯在<双城记>中写道:"这是一个最好的时代,也是一个最坏的时代."移动互联网的快速发展,出现了许多新机遇,很多创业者伺机而动 ...

  9. JAVA服务端的解码

    JAVA服务端的解码        引起乱码问题的缘由是编码与解码方法的不对称,为了能和客户端正确的交互,需要了解下服务端是何时,何处以及如何对内容进行编解码的.按照请求处理的过程,我们先来一一分析一 ...

最新文章

  1. php ajax mysql 分页查询_基于PHP_MySql_Ajax的分页技术方案
  2. 《LeetCode力扣练习》第461题 汉明距离 Java
  3. Cell Reports | 佩戴假肢后,真的能产生正确位置的触觉感吗?研究人员表示并不能...
  4. 32位微型计算机原理...,32位微型计算机原理·接口技术及其应用
  5. java经典问题算法大全_10道java经典算法!每一题都能提升你的java能力!
  6. IntelliJ IDEA Maven Mybatis generator 自动生成代码 MAC系统
  7. 高通无线网卡驱动linux,ubuntu下安装Qualcomm Atheros QCA6174无线网卡驱动方法
  8. PNG的介绍以及PNG文件解析
  9. 利用OpenGL模拟太阳系
  10. 转发 微博 Qzone 微信 草根创业英雄时代落幕:贾跃亭戴威们一去不复返
  11. 玩转Qt(14)-Qt与Web混合开发
  12. Oracle 11g用exp无法导出空表的处理方法
  13. java定时器每月月末自动执行
  14. 大数据技术之Hadoop(HDFS)第2章 HFDS的Shell操作
  15. 重启oracle服务后无法连接,解决oracle服务器重启之后连接报错的问题
  16. java kryo register_kryo处理unmodifiedList报错问题
  17. WPARAM 和 LPARAM 使用
  18. 汇编语言:伪指令整理
  19. 图像识别(一)| 从像素说起
  20. golang-vue实现微信小程序分享到朋友圈

热门文章

  1. C++ 实现无向图的最小生成树Kruskal算法(完整代码)
  2. 高等数学上-赵立军-北京大学出版社-题解-练习6.1
  3. word List32
  4. Linux下Java环境变量配置
  5. Codeforces Round #462 (Div. 2) C. A Twisty Movement dp + 思维转换
  6. 【洛谷P5385】须臾幻境/【BZOJ3514】Codechef MARCH14 GERALD07加强版【LCT】【主席树】
  7. AGC024E - Sequence Growing Hard
  8. 粉刷木板(ybtoj-单调队列)
  9. nssl1320,jzoj(初中)2108-买装备【dfs,水题】
  10. jzoj1029-电子眼【树形dp】