在Java高效编程里面看到变量一个ArrayList的时候,有两种方式:
假设a是个ArrayList
1、 for (int i=0;i<a.size();i++) {
2、 for (int i=0,n=a.size();i<n;i++) {
带着点怀疑我做了一下试验,的确是方法2快一点的,估计是a.size()方法里面花费了一点多余的时间。后来我想到jdk 1.5开始还有一种遍历的for/each方法,我做了一下比较,结果有点惊讶。
源程序如下
1import java.util.ArrayList;
2
3public class ProfileArrayList {
4
5 public static void main(String[] args) {
6 ArrayList<String> s=new ArrayList<String>();
7 for (int i=0;i<15000;i++) {
8 s.add(""+System.currentTimeMillis());
9 }
10 System.out.println("Start ");
11 testOne(s);
12 testTwo(s);
13 testThree(s);
14 System.out.println("End ");
15 }
16
17 private static void testOne(ArrayList<String> a) {
18 int j=0;String s=null;
19 for (int i=0;i<a.size();i++) {
20 s=a.get(i);
21 j++;
22 }
23 }
24
25private static void testTwo(ArrayList<String> a) {
26 int j=0;
27 String s=null;
28 for (int i=0,n=a.size();i<n;i++) {
29 s=a.get(i);
30 j++;
31 }
32 }
33
34private static void testThree(ArrayList<String> a) {
35 int j=0;
36 for (String s : a) {
37 j++;
38 }
39}
40
41}
42
通过Profiling工具看结果:
方法 运行时间
testOne 0.055764
testTwo 0.043821
testThres 0.132451
也就是说,jdk 1.5的for/each循环是最慢的。有点不相信。开头觉得是因为赋值造成的,但后来在另两个方法里面加上赋值语句,依然是for/each最慢。比较有趣的结果。
从代码清晰角度,用for/each消耗多一点点时间似乎也无所谓。但是,另两种代码也不见得“不清晰”,呵呵。看着办了。
JMeter是apache的jakarta上面的项目,用于软件的压力测试(Load Test),不但可以对HTTP,也可以对数据库(通过JDBC)、FTP、Web Service、Java 对象等等进行压力测试。
项目地址:http://jakarta.apache.org/jmeter
使用: 运行bin目录下的jmeterw.bat,运行jmeter.bat也可以,不过就会有一个命令窗口显示。
要提醒一下的是jmeter根据当前系统的locale显示菜单的语言,为了方便想设置回英文的话,可以修改jmeter.properties文件,设置language=en (我下载的2.1.1版本把“退出”误译为“推出”,怎么看都不顺眼
)
使用:
JMeter的测试计划(Test Plan)呈树状结构,树里面有多种元素类型,树状结构的元素之间有的是有继承关系的(其原理有点类似log4j)。下面简述一下元素类型:
1、
ThreadGroup
顾名思义就是线程组,测试必须有一个ThreadGroup元素作为基础(否则就没有测试线程在跑了),这个元素可以配置跑多少个线程、每个线程循环多少次,所有线程数的总启动时间(Ramp-up period)等等。
2、
Controller
包括Logical Controller和Sampler,前者用来作一些逻辑上的控制,例如轮换、条件、循环等等。Sampler就是真正“干活”的“取样器”,例如“HTTP Request”,就是拿来执行一个HTTP请求的。
3、
Listener
Listener对请求过程进行监听,可以简单理解为获取结果的东东。例如Simple Data Writer,可以把结果写到一个文本文件里(其实所有Listener都可以写数据到文件里),还有View Results in Table,就是把结果显示在表格里。
4、
Timer
用来控制执行流程中的时间延迟等功能。
5、
Assertion
断言,加到Sampler里面可以对返回的结果进行判断,例如判断HTTP返回结果里面是否含有某个字符串。如果断言为真,JMeter会标记请求为成功,否则标记为失败。
6、
Configuration Element
配置用的元素,很有用。由于测试计划是树状和有继承关系的,可以在高层次指定一个Configuration Element,低层次的相关Sampler如果没有显式地指定配置,就继承高层次的配置信息。(跟log4j很像吧?)
7、 Pre-Processor/Post-Processor Elements
用来在Sampler运行前和运行后作一些预处理和后处理工作的。例如动态修改请求的参数(预处理),从返回信息里面提取信息(后处理)等等。
举例:要做一个最简单的HTTP压力测试: 用10个线程访问一个URL,每个线程访问100次。
做法:
1、 在Test Plan下面加一个Thread Group,配置里面,线程数填10,循环次数填100
2、 在Thread Group下面加一个HTTP Request,这是一个Sampler,在它的配置里面填写主机信息,端口、协议、路径、参数等信息
3、 在HTTP Request下面加一个View Results in Table,如果你想把记录记到文件,则填写文件路径。
4、 保存一些这个Test Plan,就可以选择Run菜单下面的Run来运行了。直到Run菜单项从灰色变回黑色,就表示运行完了。在View Results in Table下面,你可以看到运行结果。
关于元素的详细描述可以参考官方文档。
JMeter功能很丰富的,还有很强的扩展能力,而且又是免费,值得研究使用。
本文只作很简要介绍,可视作备忘参考。
TPTP是eclipse官方的profiling插件,初步使用下感觉功能强大。
下载安装: 在http://www.eclipse.org/tptp/下载,我选择All-Runtime,然后像其它插件一样解压到eclipse的目录,然后允许eclipse -clean来刷新一把。
使用:
常用的profiling简单来讲就对程序运行进行记录,然后从数据中分析哪些方法运行时间长,哪些对象吃内存多,哪些类的实例多等等。一个比较好的使用入门sample在这里: http://www.eclipse.org/tptp/home/documents/tutorials/profilingtool/profilingexample_32.html 我就不罗嗦了。
值得多讲的是Remote Profiling,就是远程剖析。实现的原理是在远程机器上运行一个代理进程,要被远程剖析的程序或者Application Server启动的时候加一个JVM参数来识别这个代理进程,两者相互作用,代理就可以把收集到的信息发给在远程的一方(就是运行着eclipse的一方)。
因此要实现Remote Profiling,还要在目标机器上装一个agent。 -->
下载安装:http://www.eclipse.org/tptp/home/downloads/drops/TPTP-4.0.1.html 选择对应操作系统的Agent Controller下载,选择Runtime即可。
下载后,阅读依照getting_started.html的说明来安装即可,这里简述一下:
1、 把它的bin目录放到PATH里面
2、 运行一下SetConfig来设置参数,注意如果想让除本地localhost意外所以机器都访问的话,要注意设置Network Access Mode,默认是localhost的。
3、 运行RAStart来启动代理(Linux下)
4、 服务器端程序(例如tomcat)启动的JVM参数里面加入-XrunpiAgent:server=enabled即可(还有其它参数值参见文档)
5、 然后就可以在远程用eclipse来启动一个Profiling进程来attach到这个agent controller了。效果和在eclipse里面直接profile应用程序一样。
Volatile Fields
Sometimes, it seems excessive to pay the cost of synchronization just to read or write an instance field or two. After all, what can go wrong? Unfortunately, with modern processors and compilers, there is plenty of room for error:
-
Computers with multiple processors can temporarily hold memory values in registers or local memory caches. As a consequence, threads running in different processors may see different values for the same memory location!
-
Compilers can reorder instructions for maximum throughput. Compilers won't choose an ordering that changes the meaning of the code, but they make the assumption that memory values are only changed when there are explicit instructions in the code. However, a memory value can be changed by another thread!
If you use locks to protect code that can be accessed by multiple threads, then you won't have these problems. Compilers are required to respect locks by flushing local caches as necessary and not inappropriately reordering instructions. The details are explained in the Java Memory Model and Thread Specification developed by JSR 133 (see http://www.jcp.org/en/jsr/detail?id=133). Much of the specification is highly complex and technical, but the document also contains a number of clearly explained examples. A more accessible overview article by Brian Goetz is available at http://www-106.ibm.com/developerworks/java/library/j-jtp02244.html.
NOTE
|
Brian Goetz coined the following "synchronization motto": "If you write a variable which may next be read by another thread, or you read a variable which may have last been written by another thread, you must use synchronization." |
The volatile keyword offers a lock-free mechanism for synchronizing access to an instance field. If you declare a field as volatile, then the compiler and the virtual machine take into account that the field may be concurrently updated by another thread.
For example, suppose an object has a boolean flag done that is set by one thread and queried by another thread. You have two choices:
-
Use a lock, for example:
public synchronized boolean isDone() { return done; }
private boolean done;
(This approach has a potential drawback: the isDone method can block if another thread has locked the object.)
-
Declare the field as volatile:
public boolean isDone() { return done; }
private volatile boolean done;
Of course, accessing a volatile variable will be slower than accessing a regular variablethat is the price to pay for thread safety.
NOTE
|
Prior to JDK 5.0, the semantics of volatile were rather permissive. The language designers attempted to give implementors leeway in optimizing the performance of code that uses volatile fields. However, the old specification was so complex that implementors didn't always follow it, and it allowed confusing and undesirable behavior, such as immutable objects that weren't truly immutable. |
In summary, concurrent access to a field is safe in these three conditions:
-
The field is volatile.
-
The field is final, and it is accessed after the constructor has completed.
-
The field access is protected by a lock.