最近做邮件发送功能时,客户端操作程序等待时长,有时候会很长甚至会死线程,这个很不利于用户的体验,为了解决这个问题,我想到了使用多线程,让系统在后台发送邮件,用户操作界面不受影响,从而保证用户体验软件的流畅度。事实上完成后的效果还不错,系统界面瞬间响应,邮件后台发送,其快慢程度取决于邮件服务器响应速率和网络!
下面说说我研究多线程的方法和心得。研究时我为了便捷,只做了文件复制的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"));
}
分享到:
相关推荐
操作系统的第二个实验,Linux多线程创建
多线程例子 演示多线程使用
《MFC多线程的创建,包括工作线程和用户界面线程》全面讲解MFC多线程的创建,界面多线程与工作者多线程,多线程的起源、继承与派生,两多线程之间的区别与相同点,定时器与多线程的关系与异同(定时器是定时优先抢占...
C# winform 动态创建和 关闭多线程,基于VS2010的完整解决方案。 可运行
基于vs2013平台,创建多线程的教程,基于vs2013平台,创建多线程的教程,基于vs2013平台,创建多线程的教程
C# Winform 利用线程延时创建子窗体并且可跨线程控制主窗体的控件
如标题,在MFC中必须使用 AfxBeginThread创建多线程,如使用BeginThread可能会出现BUG
短作业多时,可使用线程池自动管理。长作业时使用线程。线程池使用,线程应用举例
否者就失去了多线程的意义) DWORD WINAPI ThreadFun(LPVOID pthread) //线程入口函数 { lpthread temp=(lpthread)pthread;//参数强制转换为结构体类型 temp->progress->SetPos(temp->pos); //设置被传递过来的...
多线程的创建和销毁。在vc 2005环境下实现的 简单的线程的创建 显示 和停止 销毁等
vc下多线程的创建 视频解析 希望大家喜欢
在Arduino中使用ProtoThread创建多线程系统
主对话框单击事件里使用多线程创建对话框B,正常情况见B对话框都是闪现一次,无法长期停留。本代码利用MSG防止了此现象的发生,适合初学多线程的新手。
JAVA多线程使用讲解,如何创建线程,讲解如何使用多线程,以及线程的等待与唤醒。 JAVA多线程使用讲解,如何创建线程,讲解如何使用多线程,以及线程的等待与唤醒。 JAVA多线程使用讲解,如何创建线程,讲解如何使用...
Linux多线程服务端编程:使用muduo+C网络库.pdf Linux多线程服务端编程:使用muduo+C网络库.pdfLinux多线程服务端编程:使用muduo+C网络库.pdfLinux多线程服务端编程:使用muduo+C网络库.pdfLinux多线程服务端编程:...
采用_beginthread创建多线程,创建尽可能多的线程,直到系统不能创建为止,创建数目实时动态显示。
使用线程池管理多线程上传,包含了文件拆分,文件分片多线程上传,单文件上传。多线程执行任务等待管理。拒绝直接new Thread创建新线程导致的诸多问题
在计算机处理器发展为包含越来越多的核心的时期,多线程是创建可伸缩性、高效的、高响应性应用程序的关键因素。如果你没有正确地使用多线程,它会导致难以捉摸的问题,需要你花费大量时间去解决。因此,现代应用程序...
VC++创建多线程 线程池 线程调度 线程同步
多线程实例,可创建任意多线程!本例子类似模拟卖火车票的流程,通过TRACE打印程序调试信息!