最近做邮件发送功能时,客户端操作程序等待时长,有时候会很长甚至会死线程,这个很不利于用户的体验,为了解决这个问题,我想到了使用多线程,让系统在后台发送邮件,用户操作界面不受影响,从而保证用户体验软件的流畅度。事实上完成后的效果还不错,系统界面瞬间响应,邮件后台发送,其快慢程度取决于邮件服务器响应速率和网络!
下面说说我研究多线程的方法和心得。研究时我为了便捷,只做了文件复制的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"));
}
分享到:
相关推荐
线程创建和管理是多线程编程中一个非常重要的概念,在实际开发中,合理地创建和管理线程可以提高程序的效率和性能。在C#中,线程池和Thread类是两种常用的线程创建和管理方式。 一、线程池(ThreadPool) 线程池是...
在计算机编程中,多线程是一种并发执行任务的技术,它允许多个子任务在同一时间运行,从而提高了程序的效率和响应性。这个“多线程例子”演示了如何在C++环境中实现多线程功能。下面我们将深入探讨多线程的基本概念...
在 VS2013 平台中,创建多线程可以使用 CreateThread 函数或 AfxBeginThread 函数。 1. 创建线程结构体 首先,需要定义一个线程结构体,用于存储线程的相关信息。例如: ```cpp typedef struct ThreadInfo { ...
- **联系**:定时器可以结合多线程使用,例如在工作者线程中设置定时器来定期执行某个任务,或者在用户界面线程中设置定时器来定期刷新界面。 #### 五、线程消息传递 在多线程环境中,线程间通信非常重要。MFC提供...
在C#编程中,Windows Forms(Winform)应用程序经常需要处理多线程,以实现并发操作,提高程序的响应性和效率。...通过学习和使用这些技术,开发者可以构建更加健壮、高效的多线程Winform应用程序。
为了实现多线程许可证的使用,开发者需要了解易语言中的线程控制命令,如“等待线程结束”、“挂起线程”和“恢复线程”等,以及同步机制的相关命令,如“创建互斥体”、“获得互斥体”和“释放互斥体”。...
本教程主要探讨的是如何利用Lucene进行索引优化,特别是通过多线程和处理多个目录来提高索引创建效率。 首先,我们需要理解Lucene的索引原理。Lucene将文档分解为词项(tokens),并对每个词项创建倒排索引。倒排...
C++11引入了`std::thread`,提供了一种更现代且更易于使用的线程创建方式。例如: ```cpp std::thread myThread(ThreadFunction, arg1, arg2); ``` `myThread`是创建的线程对象,`ThreadFunction`是线程执行的...
在C# Winform开发中,多线程技术是不可或缺的一部分,尤其当涉及到用户界面(UI)的异步操作时。线程允许程序同时执行多个任务,提高应用的响应性和效率。本话题将深入探讨如何利用线程来延迟创建子窗体,并且如何安全...
在"实验04 多线程创建实践"中,你可能需要通过编写C++代码,创建并管理多个线程,理解如何在实际应用中实现线程间的同步和通信,以及解决可能出现的并发问题。这个实践有助于你深入理解和掌握操作系统多线程的创建...
在.NET框架中,C#语言提供了强大的多线程支持,使得开发者可以充分利用现代多核处理器的优势,实现并行处理和高效能编程。本资源包含六个C#.NET多线程的实例,涵盖了多线程的基本使用到更高级的概念,如线程互斥。...
此外,Java中的线程创建方式还包括使用Runnable接口、Thread类、Executor框架等。不同的线程创建方式都有其优缺,需要根据实际情况选择合适的线程创建方式。 本文主要介绍了Java使用Callable和Future创建线程操作的...
在编程领域,多线程是实现并发执行任务的重要机制,特别是在多核处理器系统中,它能有效利用系统...在实际应用中,合理使用多线程能显著提升程序的运行效率,但也需要注意线程安全和同步问题,以避免潜在的错误和故障。
标题中的“pb真正的多线程,用createthread创建的多线程.rar”指的是PowerBuilder(PB)编程环境中实现的多线程技术,通过使用Windows API函数`CreateThread`来创建新的执行线程。在PowerBuilder中,多线程允许应用...
本篇文章将深入探讨如何在MFC中创建多线程,特别是工作者线程,并且会针对`AfxBeginThread`和`BeginThread`两种方法进行比较。 首先,我们来看标题所提到的“MFC创建多线程(工作者线程)demo”。工作者线程通常...
"多线程创建按钮"这个标题所指的,可能是一个具体的编程示例或者一个自定义控件,用于演示如何在多线程环境下创建和操作界面上的按钮。下面,我们将深入探讨多线程和创建按钮的相关知识点。 1. **多线程基础**: -...
创建多线程的方式多种多样,比如在Java中,可以使用`Thread`类的子类或者实现`Runnable`接口来创建线程。在Python中,可以使用`threading`模块的`Thread`类。在创建线程后,通常需要调用`start()`方法启动线程,这样...
《Lucene5学习之多线程创建索引》 在深入了解Lucene5的多线程索引创建之前,我们先来了解一下Lucene的基本概念。Lucene是一个高性能、全文本搜索库,由Apache软件基金会开发。它提供了强大的文本分析、索引和搜索...
总结来说,C#的Timer类提供了灵活的方式来实现在多线程环境下的定时任务,理解它们的工作原理和使用方式对于编写高效、可靠的后台服务或UI交互至关重要。通过实践,你可以更好地掌握这些知识,并在你的项目中...
在提供的"多线程创建非模态对话框防闪逝demo"中,你可以找到具体实现的代码示例,通过学习和实践,你可以更好地理解和掌握这一技术。 总结来说,MFC多线程创建非模态对话框的关键在于合理分配任务到不同的线程,...