常用的几种优化思想:池化对象、并行代替串行、负载均衡;
1、缓冲(Buffer)缓冲区是一块特定的内存区域。开辟缓冲区的目的是通过缓解应用程序上下层之间的性能差异,从而起到提高系统性能。(日常生活动的漏斗就是这个道理)
注意:缓冲可以协调下层组件和下层组件的性能差。当上层组件性能优于下层组件时,可以有效的减少上层组件对下层组件的等待时间。
缓冲最常用的场景就是提高I/O的速度。JDK中也有不少I/O组件都提供了缓冲功能。例如:BufferedWriter、BufferedOutputStream
以BufferedWriter为例: public class WriterTest {
public void test(){
try {
Writer writer = new FileWriter(new File("D:\\opt\\1.jpg"));
long begin = System.currentTimeMillis();
for(int i=0;i<1000000;i++){
writer.write(i);
}
writer.close();
System.out.println("spend:"+(System.currentTimeMillis()-begin));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void test1(){
try {
Writer writer = new BufferedWriter(new FileWriter(new File("D:\\opt\\1.jpg")));
long begin = System.currentTimeMillis();
for(int i=0;i<1000000;i++){
writer.write(i);
}
writer.close();
System.out.println("buffer_spend:"+(System.currentTimeMillis()-begin));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
WriterTest test = new WriterTest();
test.test();
test.test1();
}
}
2、缓存(Cache)
缓存(Cache)是为提升系统性能而开辟的一块内存空间。
主要作用:暂存数据处理结果,并提供下次访问使用。通过将来之不易的数据处理结果暂存起来,当有其他线程、客户端需要查询相同的数据资源时直接使用,这样就可以省略对这些数据的处理流程,而直接从缓存中获取处理结果。
HashMap就可以实现简单的缓存。(注:线程不安全的)
public class CacheTest {
//参数介绍:2000为缓存最大值;0.75f为负载因子;true为按照访问顺序,false为按照插入顺序
static LinkedHashMap<String, Object> cache = new LinkedHashMap<String, Object>(2000,0.75f,true){
private static final long serialVersionUID = 1L;
@Override
protected boolean removeEldestEntry(Map.Entry<String, Object> eldest) { //超过2000最大值时,删除最老、最不常用的数据
return size() > 2000;
}
};
public static void main(String[] args) {
CacheTest.cache.put("a", "b");
System.out.println(cache.get("a")+"==="+cache.size());
}
}
3、对象复用--池
4、并行替代串行
5、负载均衡
以网站应用为例,如果并发数非常多,单台服务器无法承受时,为保证应用程序的服务质量,就需要使用多台服务器协同工作,将系统负载尽可能均匀地分配到各个服务器节点上;当某一个服务节点宕(dang)机时,其他节点也可以保证服务正常运行。
注意:在使用负载均衡时,使用Session信息将会存在信息不同步问题,当然通过复制Session信息在所有的节点上保持一致可以解决不同步问题,但是会出现新的问题,就是很容易造成网络繁忙,影响系统效率。所以综上所述我们可以根据实际情况而定,使用cookie方式保存信息。
6、时间换空间
时间换空间通常用于嵌入式设备或者内存、硬盘空间不足的情况。通过使用牺牲CPU的方式,获得原本需要更多内存或者硬盘空间才能完成的工作。
举个非常简单的时间换空间的算法,实现a、b两个变量的值交换。交换两个变量最常用的方法是使用一个中间变量,而引入额外的变量就意味着要使用更多的空间。采用下面的方法则可以免除中间变量,而达到变量变换的目的,代价是引入更多的CPU运算。
public class SJHKJ {
public static void main(String[] args) {
int a = 1;
int b = 2;
System.out.println("a="+a+"-----b="+b);
a = a+b;
b = a-b;
a = a-b;
System.out.println("a="+a+"-----b="+b);
}
}
7、空间换时间
与时间换空间的方法相反,空间换时间则是尝试使用更多的内存或者磁盘空间换取CPU资源或者网络资源等,通过增加系统的内存消耗,来加快程序的运行速度。典型的应用实例就是缓存。
public class KJHSJ {
public static int arrayLen = 1000000;
public static void main(String[] args){
int[] a = new int[arrayLen];
int[] old = new int[arrayLen];
Map<Integer, Object> map = new HashMap<Integer, Object>();
int count = 0;
while(count<a.length){ //初始化数组数据
int value = (int)(Math.random()*arrayLen*10)+1;
if(map.get(value)==null){
map.put(value, value);
a[count] = value;
count++;
}
}
System.arraycopy(a, 0, old, 0, a.length);//这里只是为了保存原有数组
long start = System.currentTimeMillis();
Arrays.sort(a);
System.out.println("Arrays.sort spend:"+(System.currentTimeMillis()-start)+" ms");
System.arraycopy(old, 0, a, 0, old.length);//恢复原有数据
start = System.currentTimeMillis();
spaceToTime(a);
System.out.println("spaceToTime spend:"+(System.currentTimeMillis()-start)+" ms");
}
public static void spaceToTime(int[] array){
int i=0;
int max = array[0];
int l = array.length;
for(i = 1;i<l;i++){
if(array[i]>max){//找到最大值
max = array[i];
}
}
int[] temp = new int[max+1]; //分配临时空间
for(i=0;i<l;i++){
temp[array[i]] = array[i]; //以索引下标来表示数字大小
}
int j =0;
int max1=max+1;
for(i=0;i<max1;i++){ //线性复杂度
if(temp[i]>0){
array[j++] = temp[i];
}
}
}
}
使用spaceToTime()实现数据的排序耗时要比使用JDK自带的数组排序方法Arrays.sort()的排序要节省一倍的时间。(数据100万,运行在我的机器上)
之所以速度要优于JDK自带的方法,是因为它不计空间成本,以数组的索引下标来表示数据的大小,这样就避免了数字间的相互比较。
注意:如果数组中的元素不多时或者当前服务器的CPU运算能力很强,那么JDK自带的排序方法Arrays.sort()的执行速度不一定比spaceToTime()慢,视情况而定。