高性能专题

高性能编程实战

实战课题:

模拟个人账单,从数据库查询数据,输出文件;
数据准备–按日插入(本次共插入了2000条)购买商品消费记录,查询后按日统计输出文件;如下图所示
数据库图
生成目标文件
数据记录

单任务版本实现

第一个版本代码完成

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class HighCapabilityTask {
    public static void main(String[] args) {
        HighCapabilityTask task = new HighCapabilityTask();
        long starttime = System.currentTimeMillis();
//        for (int i = 0; i < 10; i++){
//            task.task();
//        }
            task.task();
        long endtime  = System.currentTimeMillis();
        System.out.println("任务执行时间:" + (endtime - starttime) + "ms");
    }
    public void task(){
        Connection con;
        String driver = "com.mysql.jdbc.Driver";
        String url = "jdbc:mysql://www.5open.top:3306/lwltestDB?useUnicode=true&characterEncoding=UTF-8;useUnicode=true&characterEncoding=UTF-8";
        String user = "lwl";
        String password = "sinodata123";
        ResultSet rs = null;
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        try {
            System.out.println("开始连接数据库" + System.currentTimeMillis());
            Class.forName(driver);
            con = DriverManager.getConnection(url, user, password);
            if (!con.isClosed()){
                System.out.println(user + "登录成功" + System.currentTimeMillis());
            }
            Statement statement = con.createStatement();
            String totalsql = "select buy_date,sum(goods_price) total_goods_price from goodsbill group by buy_date;";
            System.out.println("结束查询总计" + System.currentTimeMillis());
            rs = statement.executeQuery(totalsql);
            Map<String,Goods> totalMap = new HashMap<>();;
            //封装一个按日期统计的购买总计Map
            while (rs.next()) {
                Goods goods = new Goods();
                String key = rs.getString("buy_date");
                String total_goods_price = String.valueOf(rs.getDouble("total_goods_price"));
                goods.setBuy_date(key);
                goods.setGoods_price(total_goods_price);
                totalMap.put(key,goods);
//                System.out.println(key+"============"+goods.toString());
            }
            System.out.println("结束处理总计数据" + System.currentTimeMillis());
            //封装按日统计明细Map
            Map<String,List<Goods>> detailMap = new HashMap<>();
            String detailsql = "select goods_id,goods_name,buy_date,goods_price,goods_describe from goodsbill order by buy_date;";
            rs = statement.executeQuery(detailsql);
            System.out.println("结束明细查询" + System.currentTimeMillis());
            List<Goods> list;
            while (rs.next()){
                String goods_id = rs.getString("goods_id");
                String goods_name = rs.getString("goods_name");
                String buy_date = formatter.format(rs.getDate("buy_date"));
                String goods_price = String.valueOf(rs.getDouble("goods_price"));
                String goods_describe = rs.getString("goods_describe");
                Goods goods = new Goods();
                goods.setGoods_id(goods_id);
                goods.setGoods_name(goods_name);
                goods.setBuy_date(buy_date);
                goods.setGoods_price(goods_price);
                goods.setGoods_describe(goods_describe);
                if (detailMap.containsKey(buy_date)){
                    detailMap.get(buy_date).add(goods);
                }else {
                    list = new ArrayList<>();
                    list.add(goods);
                    detailMap.put(buy_date,list);
                }
            }
            System.out.println("结束明细数据处理" + System.currentTimeMillis());
            //输出到文件
            String baseFilePath = "C:\\00wangyi\\subjecttask\\";
            File file = new File(baseFilePath+"task"+System.currentTimeMillis());
            if (!file.exists()||!file.isDirectory()){
                file.mkdirs();
            }
            int numb = 0;
            BufferedWriter buf = null;
            for(Map.Entry<String, List<Goods>> entry : detailMap.entrySet()){
                String mapKey = entry.getKey();
                List<Goods> listdetailgoods = entry.getValue();
                File filetasktxt = new File(file.getPath()+"\\"+mapKey+"消费账单.txt");
                try {
                    filetasktxt.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                FileWriter fileWriter = new FileWriter(filetasktxt);
                buf = new BufferedWriter(fileWriter);
                buf.write("单日消费总金额:"+totalMap.get(mapKey).getGoods_price()+"元!!!\r\n");
                buf.write("购买单号\t商品名称\t商品价格\t商品描述\r\n");
                for(Goods goodsdetail : listdetailgoods){
                    buf.write(goodsdetail.getGoods_id()+"\t"+goodsdetail.getGoods_name()+"\t"+goodsdetail.getGoods_price()+"\t"+goodsdetail.getGoods_describe()+"\r\n");
//                    System.out.println(goodsdetail.toString());
                    numb++;
                }
                buf.close();
            }
            System.out.println("结束文件输出" + System.currentTimeMillis());
//            System.out.println(numb);
            rs.close();
            con.close();
        }catch (ClassNotFoundException e) {
            e.printStackTrace();
        }catch (SQLException e) {
            e.printStackTrace();
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
        }
    }
}

测试运行
在这里插入图片描述在这里插入图片描述
分析:
数据库连接时间最长,本次执行是单个任务,多线程时可采用数据库连接池优化
明细查询sql,花的时间很长,实际sql很简单,这部分可能是网络原因;
数据处理涉及代码能力,暂缓执行;
文件输出部分,可以采用ForkJoin框架优化下,此次会输出36个文件,多开几个任务可能有提升;

ForkJoin优化文件输出部分代码

优化部分代码

//输出文件for循环修改部分
  ForkJoinPool pool = new ForkJoinPool();
            Iotask iotask = new Iotask(valuesList,file,totalMap,0,valuesList.size());
            pool.invoke(iotask);//同步调用
//添加内部类实现框架功能
private static class Iotask extends RecursiveTask<Integer> {
        private final static int BASISTASK = 15;//拆分基数
        private List<List<Goods>> listsrc; //处理数据源
        private int begin;
        private int end;
        private File file;
        private Map<String,Goods> totalMap;

        public Iotask(List<List<Goods>> listsrc, File file, Map<String,Goods> totalMap, int begin, int end) {
            this.listsrc = listsrc;
            this.begin = begin;
            this.end = end;
            this.file = file;
            this.totalMap = totalMap;
        }

        @Override
        protected Integer compute() {
            if (end - begin < BASISTASK) {
                int numb = 0;
                try {
                    BufferedWriter buf = null;
                    for (int listindex = 0; listindex < listsrc.size(); listindex++){
                        String mapKey = listsrc.get(listindex).get(0).getBuy_date();
                        List<Goods> listdetailgoods = listsrc.get(listindex);
                        File filetasktxt = new File(file.getPath()+"\\"+mapKey+"消费账单.txt");
                        try {
                            filetasktxt.createNewFile();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        FileWriter fileWriter = new FileWriter(filetasktxt);
                        buf = new BufferedWriter(fileWriter);
                        buf.write("单日消费总金额:"+totalMap.get(mapKey).getGoods_price()+"元!!!\r\n");
                        buf.write("购买单号\t商品名称\t商品价格\t商品描述\r\n");
                        for(Goods goodsdetail : listdetailgoods){
                            buf.write(goodsdetail.getGoods_id()+"\t"+goodsdetail.getGoods_name()+"\t"+goodsdetail.getGoods_price()+"\t"+goodsdetail.getGoods_describe()+"\r\n");
                            numb++;
                        }
                        buf.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return numb;
            }else {
                int mid = (begin + end)/2;
                Iotask left = new Iotask (listsrc,file,totalMap,begin,mid);
                Iotask right = new Iotask (listsrc,file,totalMap,mid+1,end);
                invokeAll(left,right);
                return left.join()+right.join();
            }
        }
    }

优化后稍有提升,多次运行,处理时间在150–200ms,提升的效果很小,应该跟写文件大小的原因,单次任务的IO读写只有120K,如果是大文件,效果应该很明显;
优化fork

多任务时采用数据库连接池及线程池实现

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

public class HighCapabilityTask1 {
    public static void main(String[] args) throws SQLException, InterruptedException {
        HighCapabilityTask1 task = new HighCapabilityTask1();
        int taskNumb = 10;
        CountDownLatch latch = new CountDownLatch(taskNumb);//多线程情况下方便计算整个任务运行时间
        long starttime = System.currentTimeMillis();
        MysqlPool mysqlPool = new MysqlPool();
        ExecutorService threadPool = new ThreadPoolExecutor(4,6,3, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(10));
        System.out.println("开始执行任务"+starttime);
        for (int i = 0; i < taskNumb; i++){
            ThreadTask threadTask = new ThreadTask("task_" + i, latch, mysqlPool);
            threadPool.execute(threadTask);
        }
        latch.await();
        long endtime  = System.currentTimeMillis();
        threadPool.shutdown();
        System.out.println("任务执行时间:" + (endtime - starttime) + "ms");
    }
    static class ThreadTask implements Runnable{
        private String taskName;
        private CountDownLatch latch;
        private MysqlPool mysqlPool;
        public ThreadTask (String taskName, CountDownLatch latch, MysqlPool mysqlPool){
            this.taskName = taskName;
            this.mysqlPool = mysqlPool;
            this.latch = latch;
        }
        @Override
        public void run() {
            Connection con = null;
            ResultSet rs = null;
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
            try {
                con = mysqlPool.getConnection();
                if (!con.isClosed()){
                    System.out.println("成功拿到连接");
                }
                Statement statement = con.createStatement();
                String totalsql = "select buy_date,sum(goods_price) total_goods_price from goodsbill group by buy_date;";
                rs = statement.executeQuery(totalsql);
                Map<String,Goods> totalMap = new HashMap<>();;
                //封装一个按日期汇总的购买Map
                while (rs.next()) {
                    Goods goods = new Goods();
                    String key = rs.getString("buy_date");
                    String total_goods_price = String.valueOf(rs.getDouble("total_goods_price"));
                    goods.setBuy_date(key);
                    goods.setGoods_price(total_goods_price);
                    totalMap.put(key,goods);
    //                System.out.println(key+"============"+goods.toString());
                }
                //封装按日统计明细Map
                Map<String,List<Goods>> detailMap = new HashMap<>();
                String detailsql = "select goods_id,goods_name,buy_date,goods_price,goods_describe from goodsbill order by buy_date;";
                rs = statement.executeQuery(detailsql);
                List<Goods> list;
                while (rs.next()){
                    String goods_id = rs.getString("goods_id");
                    String goods_name = rs.getString("goods_name");
                    String buy_date = formatter.format(rs.getDate("buy_date"));
                    String goods_price = String.valueOf(rs.getDouble("goods_price"));
                    String goods_describe = rs.getString("goods_describe");
                    Goods goods = new Goods();
                    goods.setGoods_id(goods_id);
                    goods.setGoods_name(goods_name);
                    goods.setBuy_date(buy_date);
                    goods.setGoods_price(goods_price);
                    goods.setGoods_describe(goods_describe);
                    if (detailMap.containsKey(buy_date)){
                        detailMap.get(buy_date).add(goods);
                    }else {
                        list = new ArrayList<>();
                        list.add(goods);
                        detailMap.put(buy_date,list);
                    }
                }
                //输出到文件
                String baseFilePath = "C:\\00wangyi\\subjecttask\\";
                File file = new File(baseFilePath+"task"+System.currentTimeMillis());
                if (!file.exists()||!file.isDirectory()){
                    file.mkdirs();
                }
                int numb = 0;
                BufferedWriter buf = null;
                for(Map.Entry<String, List<Goods>> entry : detailMap.entrySet()){
                    String mapKey = entry.getKey();
                    List<Goods> listdetailgoods = entry.getValue();
                    File filetasktxt = new File(file.getPath()+"\\"+mapKey+"消费账单.txt");
                    try {
                        filetasktxt.createNewFile();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    FileWriter fileWriter = new FileWriter(filetasktxt);
                    buf = new BufferedWriter(fileWriter);
                    buf.write("单日消费总金额:"+totalMap.get(mapKey).getGoods_price()+"元!!!\r\n");
                    buf.write("购买单号\t商品名称\t商品价格\t商品描述\r\n");
                    for(Goods goodsdetail : listdetailgoods){
                        buf.write(goodsdetail.getGoods_id()+"\t"+goodsdetail.getGoods_name()+"\t"+goodsdetail.getGoods_price()+"\t"+goodsdetail.getGoods_describe()+"\r\n");
    //                    System.out.println(goodsdetail.toString());
                        numb++;
                    }
                    buf.close();
                }
    //            System.out.println(numb);
                rs.close();
            }catch (SQLException e) {
                e.printStackTrace();
            }catch (Exception e) {
                e.printStackTrace();
            }finally {
                latch.countDown();
                mysqlPool.releaseConnection(con);
            }
            System.out.println(taskName+"执行完成"+System.currentTimeMillis());
        }
    }
}

单线程版本多线程版本
采用连接池和线程池后,当执行任务数量为10个时执行速度差别在3s左右,任务个数越大差距越明显,这意味着,同样配置的服务器,可以处理的请求更多了。

相关代码实现

实体类

public class Goods {

    private String goods_id;
    private String goods_name;
    private String buy_date;
    private String goods_price;
    private String goods_describe;

    public String getGoods_id() {
        return goods_id;
    }

    public void setGoods_id(String goods_id) {
        this.goods_id = goods_id;
    }

    public String getGoods_name() {
        return goods_name;
    }

    public void setGoods_name(String goods_name) {
        this.goods_name = goods_name;
    }

    public String getBuy_date() {
        return buy_date;
    }

    public void setBuy_date(String buy_date) {
        this.buy_date = buy_date;
    }

    public String getGoods_price() {
        return goods_price;
    }

    public void setGoods_price(String goods_price) {
        this.goods_price = goods_price;
    }

    public String getGoods_describe() {
        return goods_describe;
    }

    public void setGoods_describe(String goods_describe) {
        this.goods_describe = goods_describe;
    }

    @Override
    public String toString() {
        return goods_id +","+ goods_name+"," +buy_date +","+ goods_price+"," + goods_describe;
    }
}

数据插入脚本

public class InsertGoodsSql {
    public static void main(String[] args) {
        InsertGoodsSql da = new InsertGoodsSql();
        da.insertGoods();
    }

    public List<String> insertGoods(){
        List<String> list = new ArrayList<>();
        LocalDate initData = LocalDate.of(2019,12,1);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        DecimalFormat df = new DecimalFormat("#.00");
        Random r = new Random();
        int max = 1000;
        String sql = "";
        for (int i = 0; i < max;){
            int m = r.nextInt(99)+1;
            i = i + m;
            if (i >= max){
                m = m - i + max;
            }
            System.out.println(""+m);
            for (int j = 1; j <= m; j++){
                double rs = r.nextDouble() * r.nextInt(1000);
                String strrs = df.format(rs);
                String nowText = initData.format(formatter);
                String daynumb = String.valueOf(j);
                if (daynumb.length()==1){
                    daynumb = "0" + daynumb;
                }
                String goods_id = nowText+daynumb;
                String goods_name = "商品"+goods_id;
                String buy_date = nowText;
                String goods_price = strrs;
                String goods_describe = "再忍不住,剁手!";
                sql = "INSERT INTO goodsbill ( goods_id, goods_name,buy_date,goods_price,goods_describe ) VALUES " +
                        "  ( '"+goods_id+"', '"+goods_name+"','"+buy_date+"',"+goods_price+" ,'"+goods_describe+"');";
//                System.out.print(nowText+daynumb+"\t"+strrs+"\t");
                System.out.println(sql);
                list.add(sql);
            }
            System.out.println("");
            initData = initData.plusDays(1);
        }
        return list;
    }
}
public class GoodsSql {
    public static void main(String[] args) {
        Connection con;
        String driver = "com.mysql.jdbc.Driver";
        String url = "jdbc:mysql://www.5open.top:3306/lwltestDB?useUnicode=true&characterEncoding=UTF-8;useUnicode=true&characterEncoding=UTF-8";
        String user = "lwl";
        String password = "sinodata123";
        int n = 0;
        try {
            Class.forName(driver);
            con = DriverManager.getConnection(url, user, password);
            if (!con.isClosed()){
                System.out.println(user + "登录成功");
            }
            Statement statement = con.createStatement();
            int rs ;
            List<String> listsql = new InsertGoodsSql().insertGoods();
            for(String strsql : listsql){
                rs = statement.executeUpdate(strsql);
                n = n + rs;
            }
            System.out.println(n);
            con.close();
        }
        catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        catch (SQLException e) {
            e.printStackTrace();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            System.out.println("已向数据库插入" + n + "条数据!");
        }
    }
}

数据库连接池简单实现

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;

public class MysqlPool {
    private static String driver = "com.mysql.jdbc.Driver";
    private static String url = "jdbc:mysql://www.5open.top:3306/lwltestDB?useUnicode=true&characterEncoding=UTF-8;useUnicode=true&characterEncoding=UTF-8";
    private static String user = "lwl";
    private static String password = "sinodata123";
    private static int initConnectSize = 3;//初始连接数
    private static int maxConnections = 5;//最大连接数
    private static LinkedList<Connection> pool = new LinkedList<>();//存放连接对象

    public MysqlPool (){
        try {
            Class.forName(driver);
            for (int i = 0; i < initConnectSize; i++){
                Connection con = DriverManager.getConnection(url, user, password);
                pool.addLast(con);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public Connection getConnection () throws SQLException{
        Connection newConnection = null;
        synchronized (pool){
            if (pool.isEmpty()){
                newConnection = DriverManager.getConnection(url, user, password);
            }else {
                newConnection = pool.removeFirst();
            }
        }
        return newConnection;
    }

    public void releaseConnection (Connection conn){
        synchronized (pool){
            pool.addLast(conn);
        }
    }

建表sql

    CREATE TABLE IF NOT EXISTS `goodsDill`(
   `goods_id` VARCHAR(10) COMMENT '商品订单号',
   `goods_name` VARCHAR(40) COMMENT '商品名称',
   `buy_date` DATE COMMENT '商品订单号',
   `goods_price` DOUBLE(10,2) COMMENT '商品价格',
   `goods_describe` VARCHAR(100) COMMENT '商品描述',
   PRIMARY KEY ( `goods_id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

高性能编程虽然是基础但特别重要,实例中的数据库连接池简陋,IO可采用通道及缓存复用思想可能优化更多,学习中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值