gogozz

导航

线程池读取SAP数据(明细导入根据物料带出具体数据)

内容以及代码参考:Java高并发核心编程(卷2):多线程、锁、JMM、JUC、高并发设计模式

 

场景:流程页面上,通过非标配置,明细表里的物料编码会自动根据RFC函数读取物料名称,描述,等级,采购组等具体数据

 

ThreadPoolExecutor线程池7大参数:

  1. corePoolSize: 核心线程数,一旦建立,不会因为超过存活时间而销毁,会一直存在复用。
  2. maximumPoolSize:最大线程数,核心线程数全部被使用了,会额外创建新线程供消费者使用,但有上限,上限就是这个最大线程数。
  3. keepAliveTime:存活时间,非核心线程,空闲时间一旦超过这个存活时间,会被销毁
  4. unit:时分秒
  5. workQueue:工作队列(阻塞队列)任务数超过最大线程数,会进入这个队列等待
  6. threadFactory:线程工厂,提供产生线程的静态工厂方法
  7. handler拒绝策略,不填会默认

  需要注意的是,corePoolSize这个参数,需要根据分类去设置大小,大概三种:CPU密集型、IO密集型、混合密集型。网上有公式,但具体要自己实测。

  因为Java8以下的线程创建,直接对应的是电脑操作OS系统的物理线程的(底层是native方法),核心线程的创建、销毁、上下文切换,都涉及到物理操作,是需要消耗物理性能的。

  另外,线程池的创建一般为单例,但是这是仅对同一个业务来说,不同的业务一般使用自己的线程池。

  例如A业务用A单例线程池,B业务用B单例线程池。不用担心这会导致创建线程过多,线程的运行虽然对应的是CPU核心,但是不等于1个线程=占用一个核心。核心是一个公共计算资源,只有线程抢占到才能使用,所以线程是可以超过CPU核心数的。

  还有IO类型的请求,其实并不占用太多的CPU计算。

 

ThreadPoolExecutor死锁问题:

  根据网上定律,一个业务(即眼前这个读取SAP数据)应该配上一个独立的线程池。不能所有业务都使用一个线程池。

  如果主业务和子业务都使用同一个线程池,可能会出现死锁阻塞情况。

  如:主业务的任务数量填充了核心线程数,并且把最大线程数也占满了,但是主任务里面有子任务,而子任务也使用同一线程池,但是此刻线程池的数量全部被主业务占满了。所以子业务就会进入阻塞队列等待。两者互相冲突,就会造成死锁阻塞。

 

ThreadPoolExecutor临界资源处理的几个方向:

  • 同步锁:synchronize、lock
  • 线程隔离:threadlocal
  • 原子类:AtomicInteger、AtomicBoolean等

  其它待后续学习补充

  

 

代码:

ThreadUtil
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;


public class ThreadUtil {

    /**
     * CPU核数
     **/
    public static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();

    /**
     * 空闲保活时限,单位秒
     */
    public static final int KEEP_ALIVE_SECONDS = 30;

    /**
     * 有界队列size
     */
    public static final int QUEUE_SIZE = 10000;

    /**
     * 混合线程池
     */
    public static final int MIXED_CORE = 0;  //混合线程池核心线程数
    public static final int MIXED_MAX = 128;  //最大线程数
    public static final String MIXED_THREAD_AMOUNT = "mixed.thread.amount";

    /**
     * 核心线程数
     */
    public static final int CORE_POOL_SIZE = 0;
    public static final int MAXIMUM_POOL_SIZE = CPU_COUNT;

    /**
     * IO线程池最大线程数
     */
    public static final int IO_MAX = Math.max(2, CPU_COUNT * 2);



    /**
     * IO线程池核心线程数
     */
    public static final int IO_CORE = 0;

    public static class CustomThreadFactory implements ThreadFactory {
        //线程池数量
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;

        //线程数量
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String threadTag;

        public CustomThreadFactory(String threadTag) {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                    Thread.currentThread().getThreadGroup();
            this.threadTag = "apppool-" + poolNumber.getAndIncrement() + "-" + threadTag + "-";
        }

        @Override
        public Thread newThread(Runnable target) {
            Thread t = new Thread(group, target,
                    threadTag + threadNumber.getAndIncrement(),
                    0);
            if (t.isDaemon()) {
                t.setDaemon(false);
            }
            if (t.getPriority() != Thread.NORM_PRIORITY) {
                t.setPriority(Thread.NORM_PRIORITY);
            }
            return t;
        }
    }

    /**
     * 线程睡眠
     *
     * @param second 秒
     */
    public static void sleepSeconds(int second) {
        LockSupport.parkNanos(second * 1000L * 1000L * 1000L);
    }

    /**
     * 线程睡眠
     *
     * @param millisecond 毫秒
     */
    public static void sleepMilliSeconds(int millisecond) {
        LockSupport.parkNanos(millisecond * 1000L * 1000L);
    }

    /**
     * 获取当前线程名称
     */
    public static String getCurThreadName() {
        return Thread.currentThread().getName();
    }

    /**
     * 获取当前线程ID
     */
    public static long getCurThreadId() {
        return Thread.currentThread().getId();
    }

    /**
     * 获取当前线程
     */
    public static Thread getCurThread() {
        return Thread.currentThread();
    }

    /**
     * 调用栈中的类名
     *
     * @return
     */
    public static String stackClassName(int level) {
//        Thread.currentThread().getStackTrace()[1]是当前方法 curClassName 执行堆栈
//        Thread.currentThread().getStackTrace()[2]就是 curClassName 的 上一级的方法堆栈 以此类推

        String className = Thread.currentThread().getStackTrace()[level].getClassName();//调用的类名
        return className;

    }

    /**
     * 调用栈中的方法名称
     *
     * @return
     */

    public static String stackMethodName(int level) {
//        Thread.currentThread().getStackTrace()[1]是当前方法 curMethodName 执行堆栈
//        Thread.currentThread().getStackTrace()[2]就是 curMethodName 的 上一级的方法堆栈 以此类推

        String className = Thread.currentThread().getStackTrace()[level].getMethodName();//调用的类名
        return className;
    }


    public static void shutdownThreadPoolGracefully(ExecutorService threadPool) {
        if (!(threadPool instanceof ExecutorService) || threadPool.isTerminated()) {
            return;
        }
        try {
            threadPool.shutdown();   //拒绝接受新任务
        } catch (SecurityException e) {
            return;
        } catch (NullPointerException e) {
            return;
        }
        try {
            // 等待 60 s,等待线程池中的任务完成执行
            if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
                // 调用 shutdownNow 取消正在执行的任务
                threadPool.shutdownNow();
                // 再次等待 60 s,如果还未结束,可以再次尝试,或则直接放弃
                if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
                    System.err.println("线程池任务未正常执行结束");
                }
            }
        } catch (InterruptedException ie) {
            // 捕获异常,重新调用 shutdownNow
            threadPool.shutdownNow();
        }
        //任然没有关闭,循环关闭1000次,每次等待10毫秒
        if (!threadPool.isTerminated()) {
            try {
                for (int i = 0; i < 1000; i++) {
                    if (threadPool.awaitTermination(10, TimeUnit.MILLISECONDS)) {
                        break;
                    }
                    threadPool.shutdownNow();
                }
            } catch (InterruptedException e) {
                System.err.println(e.getMessage());
            } catch (Throwable e) {
                System.err.println(e.getMessage());
            }
        }
    }

    /**
     * 获取执行IO密集型任务的线程池
     *
     * @return
     */
    public static ThreadPoolExecutor getIoIntenseTargetThreadPool() {
        return IoIntenseTargetThreadPoolLazyHolder.getInnerExecutor();
    }

    /**
     * 获取执行CPU密集型任务的线程池
     *
     * @return
     */
    public static ThreadPoolExecutor getCpuIntenseTargetThreadPool() {
        return CpuIntenseTargetThreadPoolLazyHolder.getInnerExecutor();
    }

    /**
     * 获取执行混合型任务的线程池     *
     *
     * @return
     */
    public static ThreadPoolExecutor getMixedTargetThreadPool() {
        return MixedTargetThreadPoolLazyHolder.getInnerExecutor();
    }
}

 

ShutdownHookThread
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import com.engine.u9integration.util.LogUtil;

public class ShutdownHookThread extends Thread {
    private volatile boolean hasShutdown = false;
    private static AtomicInteger shutdownTimes = new AtomicInteger(0);
    private final Callable callback;

    /**
     * Create the standard hook thread, with a call back, by using {@link Callable} interface.
     *
     * @param name
     * @param callback The call back function.
     */
    public ShutdownHookThread(String name, Callable callback) {
        super("JVM退出钩子(" + name + ")");

        this.callback = callback;
    }

    /**
     * Thread run method.
     * Invoke when the jvm shutdown.
     */
    @Override
    public void run() {
        synchronized (this) {
//            System.out.println(getName() + " starting.... ");
            LogUtil.log("threadpool","threadpool").info(getName() + " starting.... ");
            if (!this.hasShutdown) {
                this.hasShutdown = true;
                long beginTime = System.currentTimeMillis();
                try {
                    this.callback.call();
                } catch (Exception e) {
//                    System.out.println(getName() + " error: " + e.getMessage());
                    LogUtil.log("threadpool","threadpool").info(getName() + " error: " + e.getMessage());
                }
                long consumingTimeTotal = System.currentTimeMillis() - beginTime;
//                System.out.println(getName() + "  耗时(ms): " + consumingTimeTotal);
                LogUtil.log("threadpool","threadpool").info(getName() + "  耗时(ms): " + consumingTimeTotal);
            }
        }
    }
}

 

IoIntenseTargetThreadPoolLazyHolder
import java.util.concurrent.Callable;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import static com.engine.util.ThreadUtil.*;

public class IoIntenseTargetThreadPoolLazyHolder {

    //线程池: 用于IO密集型任务
    private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
            IO_CORE,
            IO_MAX,
            KEEP_ALIVE_SECONDS,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue(QUEUE_SIZE),
            new ThreadUtil.CustomThreadFactory("io"));

    public static ThreadPoolExecutor getInnerExecutor() {

        return EXECUTOR;
    }

    static {
//        log.info("线程池已经初始化");
        LogUtil.log("threadpool","threadpool").info("线程池已经初始化");
        LogUtil.log("threadpool","threadpool").info("IO_MAX:"+IO_MAX);
        EXECUTOR.allowCoreThreadTimeOut(true);
        //JVM关闭时的钩子函数
        Runtime.getRuntime().addShutdownHook(
                new ShutdownHookThread("IO密集型任务线程池", new Callable<Void>() {
                    @Override
                    public Void call() throws Exception {
                        //优雅关闭线程池
                        shutdownThreadPoolGracefully(EXECUTOR);
                        return null;
                    }
                }));
    }
}

 

  

CpuIntenseTargetThreadPoolLazyHolder
import java.util.concurrent.Callable;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import static com.engine.util.ThreadUtil.*;

public class CpuIntenseTargetThreadPoolLazyHolder {
    //线程池: 用于CPU密集型任务
    private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
            MAXIMUM_POOL_SIZE,
            MAXIMUM_POOL_SIZE,
            KEEP_ALIVE_SECONDS,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue(QUEUE_SIZE),
            new CustomThreadFactory("cpu"));


    public static ThreadPoolExecutor getInnerExecutor() {
        return EXECUTOR;
    }

    static {
//        log.info("线程池已经初始化");
        LogUtil.log("threadpool","threadpool").info("线程池已经初始化");

        EXECUTOR.allowCoreThreadTimeOut(true);
        //JVM关闭时的钩子函数
        Runtime.getRuntime().addShutdownHook(
                new ShutdownHookThread("IO密集型任务线程池", new Callable<Void>() {
                    @Override
                    public Void call() throws Exception {
                        //优雅关闭线程池
                        shutdownThreadPoolGracefully(EXECUTOR);
                        return null;
                    }
                }));
    }
}

 

MixedTargetThreadPoolLazyHolder
import java.util.concurrent.Callable;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import static com.engine.util.ThreadUtil.*;

public class MixedTargetThreadPoolLazyHolder {

    //首先从环境变量 mixed.thread.amount 中获取预先配置的线程数
    //如果没有对 mixed.thread.amount 做配置,则使用常量 MIXED_MAX 作为线程数
    private static final int max = (null != System.getProperty(MIXED_THREAD_AMOUNT)) ?
            Integer.parseInt(System.getProperty(MIXED_THREAD_AMOUNT)) : MIXED_MAX;
    //线程池: 用于混合型任务
    private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
            max,
            max,
            KEEP_ALIVE_SECONDS,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue(QUEUE_SIZE),
            new CustomThreadFactory("mixed"));


    public static ThreadPoolExecutor getInnerExecutor() {
        return EXECUTOR;
    }

    static {

//        log.info("线程池已经初始化");
        LogUtil.log("threadpool","threadpool").info("线程池已经初始化");


        EXECUTOR.allowCoreThreadTimeOut(true);
        //JVM关闭时的钩子函数
        Runtime.getRuntime().addShutdownHook(new ShutdownHookThread("混合型任务线程池", new Callable<Void>() {
            @Override
            public Void call() throws Exception {
                //优雅关闭线程池
                shutdownThreadPoolGracefully(EXECUTOR);
                return null;
            }
        }));
    }
}

 

YclblclImportToSap2
package com.engine.service.impl;


import com.engine.service.BaseAfter;
import com.engine.util.SapRfc;
import com.engine.util.SapUtil;
import com.engine.util.ThreadUtil;
import com.engine.u9integration.util.LogUtil;
import com.weaverboot.frame.ioc.anno.classAnno.WeaIocReplaceComponent;
import com.weaverboot.frame.ioc.anno.methodAnno.WeaReplaceAfter;
import com.weaverboot.frame.ioc.handler.replace.weaReplaceParam.impl.WeaAfterReplaceParam;
import weaver.conn.RecordSet;
import weaver.general.BaseBean;
import weaver.general.StringUtil;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;

/**
 * 成本中心领料(杂发)批量导入根据物料、存储地点、批次编号获取SAP唯一数据
 * 自动填充表单物料描述、规格等字段
 */
@WeaIocReplaceComponent("yclblclImportToSap") //如不标注名称,则按类的全路径注入
public class YclblclImportToSap2 extends BaseBean implements BaseAfter {



    //这个是接口后置方法,大概的用法跟前置方法差不多,稍有差别
    //注解名称为WeaReplaceAfter
    //返回类型必须为String
    //参数叫WeaAfterReplaceParam,这个类前四个参数跟前置方法的那个相同,不同的是多了一个叫data的String,这个是那个接口执行完返回的报文
    //你可以对那个报文进行操作,然后在这个方法里return回去
    @WeaReplaceAfter(value = "/api/workflow/reqform/doImportDetail",order = 6)
    @Override
    public String after(WeaAfterReplaceParam weaAfterReplaceParam) {
        long l1 = System.currentTimeMillis();
        /*
            用于确定是否是成本中心领料(杂发)的导入动作。
            测试环境:SELECT * from  workflow_base  where formid  = -291 对应表:formtable_main_291
            正式环境:SELECT * from  workflow_base  where formid  = -506 对应表:formtable_main_506
         */
        String data = weaAfterReplaceParam.getData();//这个就是接口执行完的报文
        String formid = weaAfterReplaceParam.getParamMap().get("formid").toString();
        String requestid = weaAfterReplaceParam.getParamMap().get("requestid").toString();
        //成本中心领料(测试中)-formid
        String yclblclFormId = getPropValue("XX","yclblclFormId");
        //成本中心领料(测试中)-表名
        String yclblclTableName = getPropValue("XX","yclblclTableName");
        //writeLog("cczxllFormId:"+cczxllFormId);
//        LogUtil.log("sap","sap").info("yclblclFormId:"+yclblclFormId.toString());
//        LogUtil.log("sap","sap").info("yclblclTableName:"+yclblclTableName.toString());
//        LogUtil.log("sap","sap").info("formid:"+formid.toString());
        if (!formid.equals(yclblclFormId)){
            return data;
        }
        RecordSet rs = new RecordSet();
        //根据物料编号、工厂、存储地点、批次编号其中三个以上条件可以在SAP查出唯一的物料库存。当然,如果不唯一,直接废弃,让用户自己去选择
        String gc = getMainDataByRequestid(requestid,yclblclTableName,2);
        String mainId = getMainDataByRequestid(requestid,yclblclTableName,1);
//        LogUtil.log("sap","sap").info("gc:"+gc.toString());
//        LogUtil.log("sap","sap").info("mainId:"+mainId.toString());

        String sql2 = "select id,pc,wlbh from "+yclblclTableName+"_dt1 where mainid = "+mainId;
//        LogUtil.log("sap","sap").info("sql2:"+sql2.toString());
        rs.execute(sql2);

        int counts = rs.getCounts();
        if (counts>200) {
            return data;
        }
        //倒数闩,需要倒数counts次
        CountDownLatch latch = new CountDownLatch(counts);
        while (rs.next()){
            String wlbh = rs.getString("wlbh");
            String ccdd = rs.getString("dccbm");

            String pc = rs.getString("pc");
            String detailId = rs.getString("id");


            //io密集型线程池处理
            ThreadUtil.getIoIntenseTargetThreadPool().submit(()->{updateOaDetailBySapData( wlbh, gc, ccdd,
                    pc, detailId, yclblclTableName,latch);});

        }
        try {
            latch.await(); // 等待倒数闩的次数减少到0,所有的线程执行完成
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long l2 = System.currentTimeMillis();
        long l = l2 - l1;
        LogUtil.log("sap","sap").info("currentTimeMillis:"+l);
        return data;
    }




    /**
     * 如果读取SAP函数有数据返回,就拿第一条数据updateOA的明细表
     * @param wlbh 物料编码
     * @param gc 工厂
     * @param ccdd 存储地点
     * @param pc 批次
     * @param detailId 明细表ID
     * @param tableName 明细表表名
     */
    private void updateOaDetailBySapData(String wlbh,String gc,String ccdd,
                                         String pc,String detailId,String tableName,CountDownLatch latch){

        try {
            LogUtil.log("sap","sap").info("updateOaDetailBySapData222:");
            SapRfc zmm_fm_002 = new SapRfc("ZMM_FM_002");
            if (!StringUtil.isEmpty(wlbh)){
                zmm_fm_002.setImportValue("IV_MATNR",wlbh);
            }
            if (!StringUtil.isEmpty(gc)){
                zmm_fm_002.setImportValue("IV_WEAKS",gc);
            }
            if (!StringUtil.isEmpty(ccdd)){
                zmm_fm_002.setImportValue("IV_LGORT",ccdd);
            }
            if (!StringUtil.isEmpty(pc)){
                zmm_fm_002.setImportValue("IV_CHARG",pc);
            }
            zmm_fm_002.execute();
            int it_base = zmm_fm_002.getExportTableCount("IT_DATA");
            LogUtil.log("sap","sap").info("it_base:"+it_base);
            if (it_base>0){
                Set<String> set = new HashSet<>();
                set.add("MAKTX");
                set.add("LABST");
                set.add("MATNR");
                set.add("MEINS");
                //set.add("LGORT"); 存储地点
                set.add("CHARG");
                set.add("ZCHGNO");
                set.add("GROES");
                set.add("ZZXH");
                set.add("ZLENGTH");
                set.add("ZTHICK");
                set.add("ZWIDTH");
                set.add("ZANGEL");
                set.add("ZPZZK");
                HashMap it_data1 = zmm_fm_002.getExportTable("IT_DATA", 0, set);
                //执行完清空client
//                SapUtil.releaseClient2(zmm_fm_002);
                StringBuffer sb = getUpdateSql(it_data1);
                String sql3 = "update "+tableName+"_dt1 set "+sb.toString()+" where id = "+detailId;
                RecordSet rs2 = new RecordSet();
//            LogUtil.log("sap","sap").info("updateOaDetailBySapData sql:"+sql3.toString());
                rs2.execute(sql3);

            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            latch.countDown();// 倒数闩减少一次
        }

    }


    /**
     * 根据requestid获取main的数据,1返回mainId,2返回工厂
     * 有缓存,不怕多次查询
     * @param requestid
     * @param tableName
     * @return
     */
    private  String getMainDataByRequestid(String requestid,String tableName,int statu){
        RecordSet rs = new RecordSet();
        String sql = "select gc,id from "+tableName+" where requestid = "+requestid;
        rs.execute(sql);
        String mainId = null;
        if (rs.next()){
            if (statu==1){
                mainId = rs.getString("id");
            }
            if (statu==2){
                mainId = rs.getString("gc");
            }
        }
        return mainId;
    }



    /**
     * 将map中要update的字段转化为SQL语句片段
     * @param map
     * @return
     */
    private StringBuffer getUpdateSql(Map map){
        StringBuffer sb = new StringBuffer();
        map.forEach((key,value)->{
            sb.append(getFields(key.toString()));
            sb.append("=");
            sb.append("'"+value+"',");
        });
        sb.deleteCharAt(sb.length()-1);
        return sb;
    }



    private String getFields(String sapFields){
        String oaFields = "";
        switch(sapFields) {
            case "MAKTX":
                oaFields = "wlms";
                break;
            case "LABST":
                oaFields =  "kcsl";
                break;
            case "MATNR":
                oaFields =  "wlbh";
                break;
            case "MEINS":
                oaFields =  "dw";
                break;
//            case "LGORT":
//                oaFields =  "ccdd";
//                break;
            case "CHARG":
                oaFields =  "pc";
                break;
            case "ZCHGNO":
                oaFields =  "ph";
                break;
            case "GROES":
                oaFields =  "gg";
                break;
            case "ZZXH":
                oaFields =  "xh";
                break;
            case "ZLENGTH":
                oaFields =  "c";
                break;
            case "ZWIDTH":
                oaFields =  "k";
                break;
            case "ZTHICK":
                oaFields =  "hd";
                break;
            case "ZANGEL":
                oaFields =  "jd";
                break;
            case "ZPZZK":
                oaFields =  "pzzk";
                break;
        }
        return oaFields;
    }
}

 

 

posted on 2023-12-22 14:26  stfzhuang  阅读(34)  评论(0编辑  收藏  举报