技术开发 频道

提升性能 Java程序优化的一些非常好的实践

  通过上述的优化之后,性能有了小幅度的提升,从 50 秒左右降到了 40 秒左右。共享数据库连接而得到的性能提升的原因是,数据库连接是一个耗时耗资源的操作,需要同远程计算机进行网络通信,建立 TCP 连接,还需要维护连接状态表,建立数据缓冲区。如果共享数据库连接,则只需要进行一次数据库连接操作,省去了多次重新建立数据库连接的时间。

  3. 针对插入数据库记录的优化

  使用预编译 SQL。具体做法是使用 java.sql.PreparedStatement 代替 java.sql.Statement 生成 SQL 语句。PreparedStatement 使得数据库预先编译好 SQL 语句,可以传入参数。而 Statement 生成的 SQL 语句在每次提交时,数据库都需进行编译。在执行大量类似的 SQL 语句时,可以使用 PreparedStatement 提高执行效率。使用 PreparedStatement 的另一个好处是不需要拼接 SQL 语句,代码的可读性更强。通过上述的优化之后,性能有了小幅度的提升,从 40 秒左右降到了 30~35 秒左右。

// 预编译 SQL 语句,执行效率高,可读性强
String sql = “insert into table1(column1,column2) values(?,?)”;
PreparedStatement pst
= con.prepareStatement(sql);
pst.setString(
1,column1Value);
pst.setString(
2,column2Value);
pst.execute();
// 需要拼接 SQL 语句,执行效率不高,代码可读性不强
StringBuilder sql
= new StringBuilder();
sql.append(
"insert into table1(column1,column2) values('");
sql.append(column1Value);
sql.append(
"','");
sql.append(column2Value);
sql.append(
"');");
Statement st;
try {
st
= con.createStatement();
st.executeUpdate(sql.toString());
} catch (SQLException e) {
e.printStackTrace();
}

  4. 针对插入数据库记录的优化

  使用 SQL 批处理。通过 java.sql.PreparedStatement 的 addBatch 方法将 SQL 语句加入到批处理,这样在调用 execute 方法时,就会一次性地执行 SQL 批处理,而不是逐条执行。通过上述的优化之后,性能有了小幅度的提升,从 30~35 秒左右降到了 30 秒左右。

  5. 针对多线程的优化

  使用多线程实现并发 / 并行。清空数据库表的操作、把从 2 个外部系统 D 取得的数据插入数据库记录的操作,是相互独立的任务,可以给每个任务分配一个线程执行。清空数据库表的操作应该先于数据库插入操作完成,可以 通过 java.lang.Thread 类的 join 方法控制线程执行的先后次序。在单核 CPU 时代,操作系统中某一时刻只有一个线程在运行,通过进程 / 线程调度,给每个线程分配一小段执行的时间片,可以实现多个进程 / 线程的并发(concurrent)执行。而在目前的多核多处理器背景下,操作系统中同一时刻可以有多个线程并行(parallel)执行,大大地提高了 计算速度。

Thread t0 = new Thread(new ClearTableTask());
Thread t1
= new Thread(new StoreServersTask(ADDRESS1));
Thread t2
= new Thread(new StoreServersTask(ADDRESS2));
try {
t0.start();
// 执行完清空操作后,再进行后续操作
t0.join();
t1.start();
t2.start();
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 断开数据库连接
try {
JdbcUtil.close();
} catch (SQLException e) {
e.printStackTrace();
}

  通过上述的优化之后,性能有了大幅度的提升,从 30 秒左右降到了 15 秒以下,10~15 秒之间。使用多线程而得到的性能提升的原因是,系统部署所在的服务器是多核多处理器的,使用多线程,给每个任务分配一个线程执行,可以充分地利用 CPU 计算资源。

  笔者试着给每个任务分配两个线程执行,希望能使程序运行得更快,但是事与愿违,此时程序运行的时间反而比每个任务分配一个线程执行的慢,大约 20 秒。笔者推测,这是因为线程较多(相对于 CPU 的内核数),使得 CPU 忙于线程的上下文切换,过多的线程上下文切换使得程序的性能反而不如之前。因此,要根据实际的硬件环境,给任务分配适量的线程执行。

  6. 针对设计模式的优化

  使用 DAO 模式抽象出数据访问层。原来的代码中混杂着 JDBC 操作数据库的代码,代码结构显得十分凌乱。使用 DAO 模式(Data Access Object Pattern)可以抽象出数据访问层,这样使得程序可以独立于不同的数据库,即便访问数据库的代码发生了改变,上层调用数据访问的代码无需改变。并且程 序员可以摆脱单调繁琐的数据库代码的编写,专注于业务逻辑层面的代码的开发。通过上述的优化之后,性能并未有提升,但是 代码的可读性、可扩展性大大地提高 了。

// DeviceDAO.java,定义了 DAO 抽象,上层的业务逻辑代码引用该接口,面向接口编程
public interface DeviceDAO {
  
public void add(Device device);
}

// DeviceDAOImpl.java,DAO 实现,具体的 SQL 语句和数据库操作由该类实现
public class DeviceDAOImpl implements DeviceDAO {
  
private Connection con;
  
public DeviceDAOImpl() {
      
// 获得数据库连接,代码略去
   }
@Override
public void add(Device device) {
      
// 使用 PreparedStatement 进行数据库插入记录操作,代码略去
   }
}

  回顾以上代码优化过程:关闭日志记录、共享数据库连接、使用预编译 SQL、使用 SQL 批处理、使用多线程实现并发 / 并行、使用 DAO 模式抽象出数据访问层,程序运行时间从最初的 100 秒左右降低到 15 秒以下,在性能上得到了很大的提升,同时也具有了更好的可读性和可扩展性。

  四、结束语

  通过该项目实例,笔者深深地感到,想要写出一个性能优化、可读性可扩展性强的程序,需要对计算机系统的基本概念、原理,编程语言的特性,软件系统 架构设计都有较深入的理解。“纸上得来终觉浅,绝知此事要躬行”,想要将这些基本理论、编程技巧融会贯通,还需要不断地实践,并总结心得体会。

0
相关文章