• 【java线程】新版基于生产者队列消费者的表迁移器


    前作:

    https://www.cnblogs.com/heyang78/p/16477819.html

    【代码下载地址】

    https://files.cnblogs.com/files/heyang78/brandnewcpmgrt_20220717.rar?t=1658015436

    【主要改进点】

    1.原来的DataQueue是手写的,实际上使用java提供的java.util.concurrent.BlockingQueue就完全够了,故减少一个类;

    2.Worker更名为Carrier,名字更贴切;

    3.DstWriter启动Carrier的方式从直接new线程改为线程池启动。

    【代码】

    数据库连接类

    package com.hy.lab.brandnewcpmgrt;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    
    /**
     * 连Oracle数据库,提供连接的工具类
     */
    class DbUtil {
        //-- 以下为连接Oracle数据库的四大参数
        private static final String DRIVER = "oracle.jdbc.OracleDriver";
        private static final String URL = "jdbc:oracle:thin:@127.0.0.1:1521/orcl";
        private static final String USER = "luna";
        private static final String PSWD = "1234";
    
        public static Connection getConn() throws Exception{
            Class.forName(DRIVER);
            Connection conn = DriverManager.getConnection(URL, USER, PSWD);
            return conn;
        }
    }

    迁移器类

    package com.hy.lab.brandnewcpmgrt;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.util.List;
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.CountDownLatch;
    
    /**
     * 基于消费者队列生产者模型的迁移器
     */
    class Migrater {
        // 成员变量:来源连接
        private Connection srcConn;
    
        // 成员变量:来源表
        private String srcTb;
    
        // 成员变量:去向连接
        private Connection dstConn;
    
        // 成员变量:去向表
        private String dstTb;
    
        // 批处理大小,可灵活调整
        private final int BATCH_SIZE=10000;
    
        // 内含写去向表工人数量,写完后srcReader会关闭来源去向连接
        private CountDownLatch cdl;
    
        /**
         * 构造函数
         *
         * @param srcConn 来源连接
         * @param srcTb   来源表
         * @param dstConn 去向连接
         * @param dstTb   去向表
         */
        public Migrater(Connection srcConn, String srcTb, Connection dstConn, String dstTb) {
            this.srcConn=srcConn;
            this.srcTb=srcTb;
            this.dstConn=dstConn;
            this.dstTb=dstTb;
    
            // 计算写去向表工人数量
            int workerCnt=(int)Math.ceil((double)getSrcCount()/(double)BATCH_SIZE);
            cdl=new CountDownLatch(workerCnt);
        }
    
        private int getSrcCount(){
            final String sql=String.format("select count(*)from %s",srcTb);
    
            try(PreparedStatement ps=srcConn.prepareStatement(sql);
            ResultSet rs=ps.executeQuery()){
                while(rs.next()){
                    return rs.getInt(1);
                }
    
                return -1;
            }catch(Exception e){
                return -2;
            }
        }
    
        /**
         * 迁移
         */
        public void migrate(){
            BlockingQueue<List<String[]>> dq=new ArrayBlockingQueue<>(5);
            SrcReader readerThread=new SrcReader(this,dq,BATCH_SIZE);
            readerThread.start();
    
            DstWriter writeThread=new DstWriter(this,dq,readerThread);
            writeThread.start();
        }
    
        public String getQuerySql(){
            return String.format("select * from %s",this.srcTb);
        }
    
        public void transfer(List<String[]> rowList) throws Exception{
            Connection conn=this.dstConn;
            conn.setAutoCommit(false);
    
            final String insertSql=String.format("insert into %s(id,f1,f2,f3,f4,f5,f6,f7,f8,f9) values(?,?,?,?,?,?,?,?,?,?)",this.dstTb);
    
            try(PreparedStatement pstmt=this.srcConn.prepareStatement(insertSql)){
                for(String[] arr:rowList){
                    for(int i=0;i<arr.length;i++){
                        String cellValue=arr[i];
                        pstmt.setString(i+1,cellValue);
                    }
    
                    pstmt.addBatch();
                }
    
                pstmt.executeBatch();
                conn.commit();
                pstmt.clearBatch();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    
        public Connection getSrcConn() {
            return srcConn;
        }
    
        public String getSrcTb() {
            return srcTb;
        }
    
        public Connection getDstConn() {
            return dstConn;
        }
    
        public String getDstTb() {
            return dstTb;
        }
    
        public CountDownLatch getCdl() {
            return cdl;
        }
    
        public static void main(String[] args){
            try{
                Connection srcConn= DbUtil.getConn();
                Connection dstConn= DbUtil.getConn();
    
                // 设定源表存在有数据,去向表存在无数据,两表表结构一致
                Migrater mgrt=new Migrater(srcConn,"emp625_from",dstConn,"emp625_to");
                mgrt.migrate();
    
                System.out.println("迁移开始");
            }catch(Exception ex){
                ex.printStackTrace();
            }
        }
    }

    源端读取类

    package com.hy.lab.brandnewcpmgrt;
    
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.TimeUnit;
    
    /**
     * Source Reader
     * 从源端读取数据的生产者
     */
    class SrcReader extends Thread{
        Migrater mgrt;
        BlockingQueue<List<String[]>> queue;
        int batchSize;
    
        public SrcReader(Migrater mgrt, BlockingQueue<List<String[]>> queue, int batchSize){
            this.mgrt=mgrt;
            this.queue=queue;
            this.batchSize=batchSize;
        }
    
        public void run(){
            final String sql=mgrt.getQuerySql();
            try(PreparedStatement pstmt=mgrt.getSrcConn().prepareStatement(sql);
                ResultSet rs=pstmt.executeQuery();){
    
                final int colCnt=10;// 假定列数为10,实际上要用Metadata取
                List<String[]> rowList=new ArrayList<>(batchSize);
    
                int count=0;
                while(rs.next()){
                    String[] arr=new String[colCnt];
    
                    for(int i=0;i<colCnt;i++){
                        arr[i]=rs.getString(i+1);
                    }
    
                    rowList.add(arr);
    
                    count++;
                    if(count==batchSize){
                        queue.put(rowList);
    
                        count=0;
                        rowList=new ArrayList<>(batchSize);
                    }
                }
    
                rs.close();
    
                if(rowList.size()>0){
                    queue.put(rowList);
                }
    
                mgrt.getCdl().await(10, TimeUnit.SECONDS);
    
                List<String[]> endLs=new ArrayList<>();
                endLs.add(null);
                queue.put(endLs);
                System.out.println("SrcReader任务完成");
    
                mgrt.getSrcConn().close();
                mgrt.getDstConn().close();
    
            }catch(Exception ex){
                ex.printStackTrace();
            }
        }
    }

    目标端写入类

    package com.hy.lab.brandnewcpmgrt;
    
    import java.util.List;
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;
    
    /**
     * Destination Writer
     * 用于启动工人向目标端写数据
     */
    class DstWriter extends Thread{
        Migrater mgrt;
        BlockingQueue<List<String[]>> queue;
        SrcReader srcReader;
    
        public DstWriter(Migrater mgrt, BlockingQueue<List<String[]>> queue, SrcReader srcReader){
            this.mgrt=mgrt;
            this.queue=queue;
            this.srcReader=srcReader;
        }
    
        public void run(){
            try{
                Executor exec= Executors.newFixedThreadPool(4);
    
                int cnt=0;
                while(true){
                    cnt++;
    
                    List<String[]> rowList=queue.take();
                    String[] arr=rowList.get(0);
    
                    if(arr!=null) {
                        //new Thread(new Carrier(mgrt, rowList)).start();
                        exec.execute(new Carrier(mgrt, rowList));
                        Thread.sleep(10);
                        System.out.println(String.format("DstWriter运作第%d次",cnt));
                    }else{
                        break;
                    }
                }
    
                System.out.println("DstWriter任务完成");
            }catch(Exception ex){
                ex.printStackTrace();
            }
        }
    }

    挑夫类

    package com.hy.lab.brandnewcpmgrt;
    
    import java.util.List;
    import java.util.concurrent.CountDownLatch;
    
    /**
     * 实际向目标端写数据的挑夫
     * 由DstWriter产生
     */
    class Carrier implements Runnable{
        Migrater mgrt;
        List<String[]> rowList;
        CountDownLatch cdl;
    
        public Carrier(Migrater mgrt, List<String[]> rowList){
            this.mgrt=mgrt;
            this.rowList=rowList;
            this.cdl=mgrt.getCdl();
        }
    
        @Override
        public void run() {
            try{
                mgrt.transfer(rowList);
                rowList=null;
            }catch(Exception ex){
                ex.printStackTrace();
            }finally {
                cdl.countDown();
            }
        }
    }

    【输出】

    迁移开始
    DstWriter运作第1次
    DstWriter运作第2次
    DstWriter运作第3次
    DstWriter运作第4次
    DstWriter运作第5次
    DstWriter运作第6次
    DstWriter运作第7次
    DstWriter运作第8次
    DstWriter运作第9次
    DstWriter运作第10次
    DstWriter运作第11次
    DstWriter运作第12次
    DstWriter运作第13次
    DstWriter运作第14次
    DstWriter运作第15次
    DstWriter运作第16次
    DstWriter运作第17次
    DstWriter运作第18次
    DstWriter运作第19次
    DstWriter运作第20次
    DstWriter运作第21次
    DstWriter运作第22次
    DstWriter运作第23次
    DstWriter运作第24次
    DstWriter运作第25次
    DstWriter运作第26次
    DstWriter运作第27次
    DstWriter运作第28次
    DstWriter运作第29次
    DstWriter运作第30次
    DstWriter运作第31次
    DstWriter运作第32次
    DstWriter运作第33次
    DstWriter运作第34次
    DstWriter运作第35次
    DstWriter运作第36次
    DstWriter运作第37次
    DstWriter运作第38次
    DstWriter运作第39次
    DstWriter运作第40次
    DstWriter运作第41次
    DstWriter运作第42次
    DstWriter运作第43次
    DstWriter运作第44次
    DstWriter运作第45次
    DstWriter运作第46次
    DstWriter运作第47次
    DstWriter运作第48次
    DstWriter运作第49次
    DstWriter运作第50次
    DstWriter运作第51次
    SrcReader任务完成
    DstWriter任务完成

    【数据库情况】

    luna@ORCL>select count(*) from emp625_to;
    
     COUNT(*----------
        500012
    
    已选择 1 行。

    【程序涉及到的表和充值语句】

    create table emp625_from(
        id number(10),
        f1 nvarchar2(10),
        f2 nvarchar2(10),
        f3 nvarchar2(10),
        f4 nvarchar2(10),
        f5 nvarchar2(10),
        f6 nvarchar2(10),
        f7 nvarchar2(10),
        f8 nvarchar2(10),
        f9 nvarchar2(10),
        primary key(id)
    );
    
    create table emp625_to(
        id number(10),
        f1 nvarchar2(10),
        f2 nvarchar2(10),
        f3 nvarchar2(10),
        f4 nvarchar2(10),
        f5 nvarchar2(10),
        f6 nvarchar2(10),
        f7 nvarchar2(10),
        f8 nvarchar2(10),
        f9 nvarchar2(10),
        primary key(id)
    );
    
    insert into emp625_from
    select rownum,
       dbms_random.string('*',dbms_random.value(1,10)),
       dbms_random.string('*',dbms_random.value(1,10)),
       dbms_random.string('*',dbms_random.value(1,10)),
       dbms_random.string('*',dbms_random.value(1,10)),
       dbms_random.string('*',dbms_random.value(1,10)),
       dbms_random.string('*',dbms_random.value(1,10)),
       dbms_random.string('*',dbms_random.value(1,10)),
       dbms_random.string('*',dbms_random.value(1,10)),
       dbms_random.string('*',dbms_random.value(1,10))
    from dual
    connect by level<500013;

    END

  • 相关阅读:
    CodeForces 404C Restore Graph (构造)
    UVa 1204 Fun Game (状压DP)
    HDU 5038 Grade (水题,坑题)
    mybatis整合Spring编码
    关于Spring MVC写的不错的几篇博客
    SpringMVC配置文件详解:<context:annotation-config/>和<context:component-scan base-package=""/>和<mvc:annotation-driven />
    常见文件下载后缀
    Spring MVC
    Spring
    反射
  • 原文地址:https://www.cnblogs.com/heyang78/p/16485874.html
Copyright © 2020-2023  润新知