分享

2.9 用Java多线程技术实现高可重用框架

 核桃酥酥 2010-10-19
2.9 用Java多线程技术实现高可重用框架
 

一、引言

可重用性是软件开发的目标之一,可重用性表现为可扩展性、可插入性和灵活性。本文通过对实例层层的分析,讨论基于Java多线程技术的可重用性软件框架的实现过程。

首先,假设目录(file1/in)下有很多文件,要求把以“A”开头的文件移动到目录(file1/ctlA)、以“B”开头的文件移动到目录(file1/ctlB)、其余的文件移动到目录(file1/out),同时,开头的字符串和移动的目录需要灵活配置。

Java开发中,常利用XML对系统进行配置,使程序更加灵活,下面是config.xml配置文件:

<?xml version="1.0" encoding="gb2312"?>

<Config>

    <welcome>welcome!</welcome>

    <ThreadService">

        <FilePath in_path="file1/in" out_path="file1/out">

            <Ctl ctl_val="A" ctl_path="file1/ctlA" />

            <Ctl ctl_val="B" ctl_path="file1/ctlB" />

        </FilePath>

    </ThreadService>

</Config>

这个XML配置文件中的in_pathout_pathctl_valctl_path非常灵活地定义了各个变量,下面Java程序就从这个配置文件读取参数,实现需求。核心代码如下:

public class Service1 {

    public static void main(String[] args) {

        String configFile = "config.xml";

       

        try {

            factory = DocumentBuilderFactory.newInstance();

            builder = factory.newDocumentBuilder();

            xmlDoc = builder.parse(configFile);

        } catch (Exception e) {

        }

        serviceList = xmlDoc.getElementsByTagName("ThreadService");

        cfg = (Element) serviceList.item(0);

        try {

            Element filePathTag = (Element) cfg

                    .getElementsByTagName("FilePath").item(0);

            inPath = filePathTag.getAttribute("in_path");

            outPath = filePathTag.getAttribute("out_path");

            NodeList list = filePathTag.getElementsByTagName("Ctl");

            ctlNum = list.getLength();

            ctlVal = new String[ctlNum];

            ctlPath = new String[ctlNum];

            for (int i = 0; i < ctlNum; i++) {

                ctlVal[i] = ((Element) list.item(i)).getAttribute("ctl_val");

                ctlPath[i] = ((Element) list.item(i)).getAttribute("ctl_path");

            }

        } catch (Exception e) {

        }

       

        while (true) {

            File inPathFolder = new File(inPath);

            msgList = inPathFolder.listFiles();

            for (int i = 0; i < msgList.length; i++) {

                String msgName = msgList[i].getName();

                tmpPath = outPath;

                for (int j = 0; j < ctlNum; j++) {

                    if (msgName.startsWith(ctlVal[j])) {

                        tmpPath = ctlPath[j];

                    }

                }

                objPath = addSep(tmpPath) + msgList[i].getName();

                // addSep(tmpPath) tmpPath末尾添加separatorChar,“/”或“\

                pathFile = new File(objPath);

                try {

                    pathFile.delete();

                    msgList[i].renameTo(pathFile);

                    System.out.println("File " + objPath + "\n");

                } catch (Exception e) {

                }

            }

        }

    }

}

Service1使用domXML配置文件获取参数,当需求中开始字符或移动目录改变时,只需要修改XML配置文件,而不需要修改Java程序。可见,这个程序已经具备了一定的灵活性。

二、线程的引入

Service1已经具备了一定的灵活性,但对于多个目录的文件按文件名的开头字符串进行分发,就比较麻烦。这种需求描述如下:目录(file1/in)下有很多文件,要求把以“A”开头的文件移动到目录(file1/ctlA)、以“B”开头的文件移动到目录(file1/ctlB)、其余的文件移动到目录(file1/out)。目录(file2/in)下有很多文件,要求把以“A”开头的文件移动到目录(file2/ctlA)、以“B”开头的文件移动到目录(file2/ctlB)、其余的文件移动到目录(file2/out)。

针对这样的需求,需要准备两个config.xml文件、并启用两个Service1进程。这样既不方便,又消耗大量资源。Java中的线程开销比进程小,而且线程个数的定义、线程的启动和停止也非常灵活。所以,把Java多线程技术引入框架,以提高程序的可扩展性。

Service1分为两个部分,一部分用于读取config.xml配置信息,并管理线程,另外一部分用于定义线程。同时,config.xml文件也做简单修改,代码如下:

<?xml version="1.0" encoding="gb2312"?>

<Config>

    <welcome>welcome!</welcome>

    <ThreadService class="org.run.Service2">

        <FilePath in_path="file1/in" out_path="file1/out">

            <Ctl ctl_val="A" ctl_path="file1/ctlA" />

            <Ctl ctl_val="B" ctl_path="file1/ctlB" />

        </FilePath>

    </ThreadService>

    <ThreadService class="org.run.Service2">

        <FilePath in_path="file2/in" out_path="file2/out">

            <Ctl ctl_val="A" ctl_path="file2/ctlA" />

            <Ctl ctl_val="B" ctl_path="file2/ctlB" />

        </FilePath>

    </ThreadService>   

</Config>

 

修改后的线程Service2的核心代码如下:

public class Service2 extends Thread {

   

    public void initial(Element cfgInfo) throws Exception {

        this.cfg = cfgInfo;

        try {

            initParameter();    // 读取config.xml参数

        } catch (Exception e) {

            throw e;

        }

    }

    public void run() {

        try {

            bizService();

        } catch (Exception e) {

        }

    }

    protected void bizService() throws Exception {

       

        while (true) {

            try {

                File inPathFolder = new File(inPath);

                msgList = inPathFolder.listFiles();

            } catch (Exception e) {

                continue;

            }

            for (int i = 0; i < msgList.length; i++) {

                tmpPath = ctlFilePath(msgList[i].getName());

                // ctlFilePath返回该文件待移动的目录

                objPath = addSep(tmpPath) + msgList[i].getName();

                pathFile = new File(objPath);

                try {

                    pathFile.delete();

                    msgList[i].renameTo(pathFile);

                    System.out.println("ctl path File " + objPath + "\n");

                } catch (Exception e) {

                }

            }

        }

    }

}

管理线程的类Control

public class Control {

    public static void main(String[] args) {

        String configFile = "config.xml";

       

        try {

            factory = DocumentBuilderFactory.newInstance();

            builder = factory.newDocumentBuilder();

            xmlDoc = builder.parse(configFile);

        } catch (Exception e) {

        }

        serviceList = xmlDoc.getElementsByTagName("ThreadService");

        for (int i = 0; i < serviceList.getLength(); i++) {

            serviceCfg = (Element) serviceList.item(i);

            try {

                Service3 service = (Service3) Class.forName(

                        serviceCfg.getAttribute("class")).newInstance();

                service.setName("service");

                service.initial(serviceCfg);

                service.start();

            } catch (Exception e) {

            }

        }

    }

}

框架的主要变化在于,将Service定义成线程形式,然后通过定义的Control类读取config.xml中的参数、管理线程。这样,这个框架既可以任意配置开头字符串和目录,又可以配置移动的并发数。可见,框架的扩展性和灵活性有了进一步提高。

三、线程的泛化

Service2Service1更加灵活,但如果一个目录按照开头字符串移动,另一个目录按照结尾的字符串移动,那么就必须修改框架的管理类Control。为了使新的应用能够方便地插入这个框架,在ControlService之间加入一个抽象层ServiceThreadControl只调用ServiceThread,而新插入的应用都继承自ServiceThread抽象类。

抽象类ServiceThread的定义如下:

public class ServiceThread extends Thread {

   

    public void run() {

    }

   

}

具体应用子类Service3继承自ServiceThread,实现的代码如下:

public class Service3 extends ServiceThread {

   

    public void initial(Element cfgInfo) throws Exception {

        this.cfg = cfgInfo;

        try {

            initParameter();    // 获取config.xml的参数

        } catch (Exception e) {

        }

    }

    public void run() {

        try {

            bizService();

        } catch (Exception e) {

        }

    }

    protected void bizService() throws Exception {

       

        while (true) {

            File inPathFolder = new File(inPath);

            msgList = inPathFolder.listFiles();

            for (int i = 0; i < msgList.length; i++) {

                tmpPath = ctlFilePath(msgList[i].getName());

                objPath = addSep(tmpPath) + msgList[i].getName();

                pathFile = new File(objPath);

                try {

                    pathFile.delete();

                    msgList[i].renameTo(pathFile);

                    System.out.println("ctl path File " + objPath + "\n");

                } catch (Exception e) {

                }

            }

        }

    }

}

管理类Control不直接调用应用子类,而是通过抽象父类ServiceThread调用子类,实现的代码如下:

public class Control {

    public static void main(String[] args) {

 

        String configFile = "config.xml";

        try {

            factory = DocumentBuilderFactory.newInstance();

            builder = factory.newDocumentBuilder();

            xmlDoc = builder.parse(configFile);

        } catch (Exception e) {

            System.out.println("Get config.xml Error:" + e.toString());

            return;

        }

        serviceList = xmlDoc.getElementsByTagName("ThreadService");

        for (int i = 0; i < serviceList.getLength(); i++) {

            serviceCfg = (Element) serviceList.item(i);

            try {

                ServiceThread service = (ServiceThread) Class.forName(

                        serviceCfg.getAttribute("class")).newInstance();

                service.setName("service");

                service.initial(serviceCfg);

                service.start();

            } catch (Exception e) {

                System.out.println("Start Service Error:" + e.toString());

            }

        }

    }

}

四、结语

应用程序开发中,软件的扩展性、可插入和灵活性非常重用。本文使用Java中的XML配置和多线程技术实现了一个重用性比较好的软件框架。当然,这个框架还需要进一步优化,特别是线程启动、监控和终止等的管理。

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多