举一个例子,我们做项目需要安排计划,每一个模块可以由多人同时并行做多项任务,也可以一个人或者多个人串行工作,但始终会有一条关键路径,这条路径就是项目的工期。系统一次调用的响应时间跟项目计划一样,也有一条关键路径,这个关键路径是就是系统影响时间。关键路径由 CPU 运算、IO、外部系统响应等等组成。
对于一个系统的用户来说,从用户点击一个按钮、链接或发出一条指令开始,到系统把结果以用户希望的形式展现出来为终止,整个过程所消耗的时间是用户对这个软件性能的直观印象,也就是我们所说的响应时间。当响应时间较短时,用户体验是很好的,当然用户体验的响应时间包括个人主观因素和客观响应时间。在设计软件时,我们就需要考虑到如何更好地结合这两部分达到用户最佳的体验。如:用户在大数据量查询时,我们可以将先提取出来的数据展示给用户,在用户看的过程中继续进行数据检索,这时用户并不知道我们后台在做什么,用户关注的是用户操作的响应时间。
我们经常说的一个系统吞吐量,通常由 QPS(TPS)、并发数两个因素决定,每套系统这两个值都有一个相对极限值,在应用场景访问压力下,只要某一项达到系统最高值,系统的吞吐量就上不去了,如果压力继续增大,系统的吞吐量反而会下降,原因是系统超负荷工作,上下文切换、内存等等其它消耗导致系统性能下降,决定系统响应时间要素。
缓冲 (Buffer)缓冲区是一块特定的内存区域,开辟缓冲区的目的是通过缓解应用程序上下层之间的性能差异,提高系统的性能。在日常生活中,缓冲的一个典型应用是漏斗。缓冲可以协调上层组件和下层组件的性能差,当上层组件性能优于下层组件时,可以有效减少上层组件对下层组件的等待时间。基于这样的结构,上层应用组件不需要等待下层组件真实地接受全部数据,即可返回操作,加快了上层组件的处理速度,从而提升系统整体性能。
使用 BufferedWriter 进行缓冲BufferedWriter 就是一个缓冲区用法,一般来说,缓冲区不宜过小,过小的缓冲区无法起到真正的缓冲作用,缓冲区也不宜过大,过大的缓冲区会浪费系统内存,增加 GC 负担。尽量在 I/O 组件内加入缓冲区,可以提高性能。一个缓冲区例子代码如清单 1 所示。
清单 1. 加上缓冲区之前示例代码 import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import javax.swing.JApplet; public class NoBufferMovingCircle extends JApplet implements Runnable{ Image screenImage = null; Thread thread; int x = 5; int move = 1; public void init(){ screenImage = createImage(230,160); } public void start(){ if(thread == null){ thread = new Thread(this); thread.start(); } } @Override public void run() { // TODO Auto-generated method stub try{ System.out.println(x); while(true){ x+=move; System.out.println(x); if((x>105)||(x<5)){ move*=-1; } repaint(); Thread.sleep(10); } }catch(Exception e){ } } public void drawCircle(Graphics gc){ Graphics2D g = (Graphics2D) gc; g.setColor(Color.GREEN); g.fillRect(0, 0, 200, 100); g.setColor(Color.red); g.fillOval(x, 5, 90, 90); } public void paint(Graphics g){ g.setColor(Color.white); g.fillRect(0, 0, 200, 100); drawCircle(g); } }程序可以完成红球的左右平移,但是效果较差,因为每次的界面刷新都涉及图片的重新绘制,这较为费时,因此,画面的抖动和白光效果明显。为了得到更优质的显示效果,可以为它加上缓冲区。代码如清单 2 所示。
清单 2. 加上缓冲区之后示例代码 import java.awt.Color; import java.awt.Graphics; public class BufferMovingCircle extends NoBufferMovingCircle{ Graphics doubleBuffer = null;//缓冲区 public void init(){ super.init(); doubleBuffer = screenImage.getGraphics(); } public void paint(Graphics g){//使用缓冲区,优化原有的 paint 方法 doubleBuffer.setColor(Color.white);//先在内存中画图 doubleBuffer.fillRect(0, 0, 200, 100); drawCircle(doubleBuffer); g.drawImage(screenImage, 0, 0, this); } } 使用 Buffer 进行 I/O 操作除 NIO 外,使用 Java 进行 I/O 操作有两种基本方式:
使用基于 InputStream 和 OutputStream 的方式;
使用 Writer 和 Reader。
无论使用哪种方式进行文件 I/O,如果能合理地使用缓冲,就能有效地提高 I/O 的性能。
下面显示了可与 InputStream、OutputStream、Writer 和 Reader 配套使用的缓冲组件。
OutputStream-FileOutputStream-BufferedOutputStream
InputStream-FileInputStream-BufferedInputStream
Writer-FileWriter-BufferedWriter
Reader-FileReader-BufferedReader