`

多线程的创建和使用

    博客分类:
  • java
阅读更多
   最近做邮件发送功能时,客户端操作程序等待时长,有时候会很长甚至会死线程,这个很不利于用户的体验,为了解决这个问题,我想到了使用多线程,让系统在后台发送邮件,用户操作界面不受影响,从而保证用户体验软件的流畅度。事实上完成后的效果还不错,系统界面瞬间响应,邮件后台发送,其快慢程度取决于邮件服务器响应速率和网络!
下面说说我研究多线程的方法和心得。研究时我为了便捷,只做了文件复制的Demo,然后再将功能代码移植到开发系统的邮件发送功能中,这样做是为了保证系统的安全性。
首先要知道使用多线程有两种方法,一是继承Thread类,一种是实现Runnable接口。由于继承的局限性,只能单继承,所以一般情况下会先考虑实现Runnable接口这种。
我们要开启一个线程,那么必须先给他一个入口即run()方法,run()方法里可以写业务处理方法,这样一个线程就能流通并处理业务了。具体代码如下:
/**
* 所有线程任务接口
* 其他线程任务必须继承该类
*
* @author hadestage
*/
public abstract class Task implements Runnable {
    /**
    * 任务执行入口
    */
    public void run() {
    try {
taskCore();
} catch (Exception e) {
e.printStackTrace();
}
    }

    /**
    * 所有任务的核心业务逻辑执行,哪个业务需要开启多线程就继承该类,然后重写该方法的业务功能
    *
    * @throws Exception
    */
    public abstract Task[] taskCore() throws Exception;

    /**
    * 是否需要立即执行
    */
    protected abstract boolean needExecuteImmediate();
}
这个线程任务类是我们业务功能启用多线程时可继承的接口,继承时只需重写业务方法
taskCore()即可。

作为示例Demo,我创建了一个文件复制的线程任务,具体代码如下:
/**
* 创建文件线程任务
*
* @author hadestage
*/
public class FileCreatorTask extends Task {

// 源文件名和目标文件名 (Demo用的是全路径)
public String sourceFileName;
public String targeFileName;

public FileCreatorTask(String sourceFileName,String targeFileName){
this.sourceFileName = sourceFileName;
this.targeFileName = targeFileName;
}

/**
* 执行创建任务动作
*/
@Override
public Task[] taskCore(){
try {
FileUtil.copyFile(new File(sourceFileName), new File(targeFileName));
} catch (IOException e) {
this.taskCore();
}
return null;
}
/**
    * 是否需要立即执行
    *
    * @return false
    */
@Override
protected boolean needExecuteImmediate() {
return false;
}
}
其中FileUtil.copyFile(new File(sourceFileName), new File(targeFileName));
就是我们的关键业务代码,复制文件。

有时候,我们复制文件、上传文件、发送邮件等不仅仅只有一个任务,很有可能多个复制、上传、发送任务同时进行,这时我们开启一个线程好像有些不够用了,总不能一个一个的线程新建吧。那么我们就需要同时开启多个线程了,这就涉及到线程池的概念了,就像数据库连接池那样,建一个线程池,里面设定线程数,这样就能解决多线程任务了。
/**
* 线程池
* 创建线程池,销毁线程池,添加新任务
*
* @author hadestage
*/
public final class ThreadPool {
    /* 单例 */
    private static ThreadPool instance = null;
    /* 默认池中线程数 */
    public static int worker_num = 5;
    private static List<Task> taskQueue = Collections.synchronizedList(new LinkedList<Task>());
    /* 池中的所有线程 */
    public PoolWorker[] workers;

   ... ... ...
}
创建单例线程池,从而保证线程池只有一个且外部无法创建新的线程池,设置池中默认线程数,但支持手动设置线程数,PoolWorker是当前类的内部类,它充当线程池里的线程,下文会有介绍。

接下来就是在ThreadPool这个线程池类中添加方法,使它能在创建线程池时设置线程数。
// 默认线程5个
    private ThreadPool() {
        workers = new PoolWorker[worker_num];
        for (int i = 0; i < workers.length; i++) {
            workers[i] = new PoolWorker(i);
        }
    }
    private ThreadPool(int pool_worker_num) {
        worker_num = pool_worker_num;
        workers = new PoolWorker[worker_num];
        for (int i = 0; i < workers.length; i++) {
            workers[i] = new PoolWorker(i);
        }
    }
    // 创建默认线程数
    public static synchronized ThreadPool getInstance() {
        if (instance == null)
            return new ThreadPool();
        return instance;
    }
    // 创建规定的线程个数
    public static synchronized ThreadPool getInstance(int threadNumber) {
        if (instance == null)
            return new ThreadPool(threadNumber);
        return instance;
}

    创建完线程池,开启了需要的线程,就得提供能往线程池里增加任务并执行任务的方法。
/**
    * 增加新的任务
    * 每增加一个新任务,都要唤醒任务队列
    * @param newTask
    */
    public void addTask(Task newTask) {
        synchronized (taskQueue) {
            taskQueue.add(newTask);
            /* 唤醒队列, 开始执行 */
            taskQueue.notifyAll();
        }
}
    /**
    * 批量增加新任务
    * @param taskes
    */
    public void batchAddTask(List<Task> taskes) {
        if (taskes == null || taskes.size() == 0) return;
        synchronized (taskQueue) {
            for (int i = 0; i < taskes.size(); i++) {
                if (taskes.get(i) == null) {
                    continue;
                }
                taskQueue.add(taskes.get(i));
            }
            /* 唤醒队列, 开始执行 */
            taskQueue.notifyAll();
        }
        for (int i = 0; i < taskes.size(); i++) {
            if (taskes.get(i) == null) {
                continue;
            }
        }
    }
  
任务执行完成后,我们还需要做一项工作,总不能让线程池里的线程一直占着坑不放吧,你用完了,别人还等着用呢,所以我们还得销毁线程池,等要用了再调用就行了。
/**
    * 销毁线程池
    */
    public synchronized void destroy() {
        for (int i = 0; i < worker_num; i++) {
            workers[i].stopWorker();
            workers[i] = null;
        }
        taskQueue.clear();
    }

线程池ThreadPool类中的内部类,该类的作用是具体化线程池,一个任务是否执行,何时执行,怎么执行都是由这里面进行统一调配的。
    class PoolWorker extends Thread {
        private int index = -1;
        /* 该工作线程是否有效 */
        private boolean isRunning = true;
        /* 该工作线程是否可以执行新任务 */
        private boolean isWaiting = true;

        public PoolWorker(int index) {
        this.setName("eastCompeacePoolWorker-"+index);
            this.index = index;
            start();
        }
        public void setIsRunning(boolean isRunning){
        this.isRunning = isRunning;
        }
        public void stopWorker() {
            this.isRunning = false;
        }
        public boolean isWaiting() {
            return this.isWaiting;
        }
        /**
        * 循环执行任务
        * 这也许是线程池的关键所在
        */
        public void run() {
            while (isRunning) {
            System.out.println("index: "+index);
                Task r = null;
                synchronized (taskQueue) {
                    while (taskQueue.isEmpty()) {
                        try {
                            /* 任务队列为空,则等待有新任务加入从而被唤醒 */
                            taskQueue.wait(20);
                        } catch (InterruptedException ie) {
                        ie.printStackTrace();
                        }
                    }
                    /* 取出任务执行 */
                    r = (Task) taskQueue.remove(0);
                }
                if (r != null) {
                    isWaiting = false;
                    try {
                        /* 该任务是否需要立即执行 */
                        if (r.needExecuteImmediate()) {
                            new Thread(r).start();
                        } else {
                            r.run();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    isWaiting = true;
                    r = null;
                }
            }
        }
}

以上就是对线程池的创建和应用。现在有了业务功能,有了任务,也有了创建线程池和线程的方法,接下来就可以测试下了,可以新建一个带有main()函数的类,在main()方法里创建线程池,然后添加任务即可了。具体代码如下:
public static void main(String[] args) {
ThreadPool threadPool = ThreadPool.getInstance();
threadPool.addTask(new FileCreatorTask("D:\\pic.jpg","E:\\"+ System.currentTimeMillis()+".jpg"));
}
1
2
分享到:
评论

相关推荐

    线程创建和管理

    线程创建和管理是多线程编程中一个非常重要的概念,在实际开发中,合理地创建和管理线程可以提高程序的效率和性能。在C#中,线程池和Thread类是两种常用的线程创建和管理方式。 一、线程池(ThreadPool) 线程池是...

    多线程例子 演示多线程使用

    在计算机编程中,多线程是一种并发执行任务的技术,它允许多个子任务在同一时间运行,从而提高了程序的效率和响应性。这个“多线程例子”演示了如何在C++环境中实现多线程功能。下面我们将深入探讨多线程的基本概念...

    vs2013创建多线程教程

    在 VS2013 平台中,创建多线程可以使用 CreateThread 函数或 AfxBeginThread 函数。 1. 创建线程结构体 首先,需要定义一个线程结构体,用于存储线程的相关信息。例如: ```cpp typedef struct ThreadInfo { ...

    MFC多线程的创建,包括工作线程和用户界面线程

    - **联系**:定时器可以结合多线程使用,例如在工作者线程中设置定时器来定期执行某个任务,或者在用户界面线程中设置定时器来定期刷新界面。 #### 五、线程消息传递 在多线程环境中,线程间通信非常重要。MFC提供...

    C# winform动态创建和关闭多线程源码 可运行

    在C#编程中,Windows Forms(Winform)应用程序经常需要处理多线程,以实现并发操作,提高程序的响应性和效率。...通过学习和使用这些技术,开发者可以构建更加健壮、高效的多线程Winform应用程序。

    易语言多线程许可证使用

    为了实现多线程许可证的使用,开发者需要了解易语言中的线程控制命令,如“等待线程结束”、“挂起线程”和“恢复线程”等,以及同步机制的相关命令,如“创建互斥体”、“获得互斥体”和“释放互斥体”。...

    lucene索引优化多线程多目录创建索引

    本教程主要探讨的是如何利用Lucene进行索引优化,特别是通过多线程和处理多个目录来提高索引创建效率。 首先,我们需要理解Lucene的索引原理。Lucene将文档分解为词项(tokens),并对每个词项创建倒排索引。倒排...

    多线程的创建 vc

    C++11引入了`std::thread`,提供了一种更现代且更易于使用的线程创建方式。例如: ```cpp std::thread myThread(ThreadFunction, arg1, arg2); ``` `myThread`是创建的线程对象,`ThreadFunction`是线程执行的...

    C# Winform线程创建子窗体

    在C# Winform开发中,多线程技术是不可或缺的一部分,尤其当涉及到用户界面(UI)的异步操作时。线程允许程序同时执行多个任务,提高应用的响应性和效率。本话题将深入探讨如何利用线程来延迟创建子窗体,并且如何安全...

    操作系统多线程的创建

    在"实验04 多线程创建实践"中,你可能需要通过编写C++代码,创建并管理多个线程,理解如何在实际应用中实现线程间的同步和通信,以及解决可能出现的并发问题。这个实践有助于你深入理解和掌握操作系统多线程的创建...

    C#.NET多线程实例6个(包括多线程基本使用,多线程互斥等全部多线程使用实例),可直接运行

    在.NET框架中,C#语言提供了强大的多线程支持,使得开发者可以充分利用现代多核处理器的优势,实现并行处理和高效能编程。本资源包含六个C#.NET多线程的实例,涵盖了多线程的基本使用到更高级的概念,如线程互斥。...

    Java使用Callable和Future创建线程操作示例

    此外,Java中的线程创建方式还包括使用Runnable接口、Thread类、Executor框架等。不同的线程创建方式都有其优缺,需要根据实际情况选择合适的线程创建方式。 本文主要介绍了Java使用Callable和Future创建线程操作的...

    多线程创建于销毁实现代码

    在编程领域,多线程是实现并发执行任务的重要机制,特别是在多核处理器系统中,它能有效利用系统...在实际应用中,合理使用多线程能显著提升程序的运行效率,但也需要注意线程安全和同步问题,以避免潜在的错误和故障。

    pb真正的多线程,用createthread创建的多线程.rar

    标题中的“pb真正的多线程,用createthread创建的多线程.rar”指的是PowerBuilder(PB)编程环境中实现的多线程技术,通过使用Windows API函数`CreateThread`来创建新的执行线程。在PowerBuilder中,多线程允许应用...

    MFC创建多线程(工作者线程)demo

    本篇文章将深入探讨如何在MFC中创建多线程,特别是工作者线程,并且会针对`AfxBeginThread`和`BeginThread`两种方法进行比较。 首先,我们来看标题所提到的“MFC创建多线程(工作者线程)demo”。工作者线程通常...

    多线程创建按钮

    "多线程创建按钮"这个标题所指的,可能是一个具体的编程示例或者一个自定义控件,用于演示如何在多线程环境下创建和操作界面上的按钮。下面,我们将深入探讨多线程和创建按钮的相关知识点。 1. **多线程基础**: -...

    多线程实例,可创建任意多线程

    创建多线程的方式多种多样,比如在Java中,可以使用`Thread`类的子类或者实现`Runnable`接口来创建线程。在Python中,可以使用`threading`模块的`Thread`类。在创建线程后,通常需要调用`start()`方法启动线程,这样...

    Lucene5学习之多线程创建索引

    《Lucene5学习之多线程创建索引》 在深入了解Lucene5的多线程索引创建之前,我们先来了解一下Lucene的基本概念。Lucene是一个高性能、全文本搜索库,由Apache软件基金会开发。它提供了强大的文本分析、索引和搜索...

    C# Timer的多线程使用方法

    总结来说,C#的Timer类提供了灵活的方式来实现在多线程环境下的定时任务,理解它们的工作原理和使用方式对于编写高效、可靠的后台服务或UI交互至关重要。通过实践,你可以更好地掌握这些知识,并在你的项目中...

    MFC多线程创建非模态对话框(防止一闪而逝)

    在提供的"多线程创建非模态对话框防闪逝demo"中,你可以找到具体实现的代码示例,通过学习和实践,你可以更好地理解和掌握这一技术。 总结来说,MFC多线程创建非模态对话框的关键在于合理分配任务到不同的线程,...

Global site tag (gtag.js) - Google Analytics