Java带参数的线程类ParameterizedThread——即如何给Thread传递参数
在Java中似乎没有提供带运行参数的线程实现类,在第三方类库中也没有找到。网上有大量的文章在讨论这个问题,但都没有提供很好的代码封装解决方案,这令我很吃惊。如果读者知道有官方或者第三方的实现方式,欢迎留言说明。本文最后给出了一种实现带运行参数的线程实现类。
在C#的基础类库中早就提供了相关的解决方案,如下是C#几种常见的带参数子线程创建方法:
Thread th = new Thread((param) =>
{
Console.WriteLine(param);
});
th.Start(i); Task.Factory.StartNew((param) =>
{
Console.WriteLine(param);
}, i); ThreadPool.QueueUserWorkItem((param) =>
{
Console.WriteLine(param);
}, i);
让我们从一个实际编码问题开始讲起,主线程循环一个集合元素,并创建子线程中做相应的处理(可能比较耗时)。下面是最初的一段实现代码,请问这段代码存在什么问题?
for (int i = 0; i < 100; i++) {
Thread th = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(i + "");
}
});
th.start();
}
(1)首先这段程序是无法通过编译的,在intellij idea中提示“Variable 'i' is accessed from within inner class,needs to be final or effectively final”,在Eclipse中提示"Local variable i defined in an enclosing scope must be final or effectively final",意思是说在内部类中无法访问外部类中不是final修饰的成员变量。那么我们很容易想通过下面的方式解决:
for (int i = 0; i < 100; i++) {
int p = i;
Thread th = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(p + "");
}
});
th.start();
}
这段代码能够通过编译,而且似乎运行良好。但是不是线程安全的,父线程中的循环变量不断被修改,子线程得到的父线程成员变量可能是不正确的。
(2)其次上面的代码在循环体内创建了大量的子线程,线程的创建和销毁会造成系统资源的开销,一般推荐使用线程池的方式创建线程,比如ThreadPoolExecutor。
ThreadPoolExecutor executor = new ThreadPoolExecutor(6, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
for (int i = 0; i < 100; i++) {
int p=i;
executor.execute(()->{
System.out.println(p + "");
});
}
那么有什么办法使得子线程能够安全的获取到父线程的变量呢,我们可以编写如下的线程实现类:
public class MyRunnable implements Runnable {
Object param;
public MyRunnable(Object parameter) {
this.param = parameter;
}
@Override
public void run() {
System.out.println(param.toString());
}
}
这里的问题是我们必须针对不同的情形,编写不同的子线程实现类,在各个工程中分散了很多类似的脚手架代码,闻到这种“味道”,我们应该想到需要进行代码抽象和封装,以便于重复使用。据此,笔者用Java封装了一个带参数的线程类:
/**
* ParameterizedThreadStart defines the start method for starting a thread.
* @author wadexmy
* @param <T>
*/
public interface ParameterizedThreadStart<T>{
/**
* a method with parameter
* @param context
*/
void run(T context);
}
/**
* ParameterizedThread defines a thread with a generic parameter
* @author wadexmy
* @param <T>
*/
public class ParameterizedThread<T> implements Runnable{ private T context;
private ParameterizedThreadStart<T> parameterStart; /**
* Constructor
* @param context
*/
public ParameterizedThread(T context,ParameterizedThreadStart<T> parameterStart){
this.context=context;
this.parameterStart=parameterStart;
} /**
* getContext returns the context of current thread.
* @return
*/
public T getContext(){
return context;
} /**
* run method to be called in that separately executing thread.
*/
@Override
public void run() {
parameterStart.run(context);
}
}
类ParameterizedThread实现了 Runnable,在构造方法中传递了一个参数和需要执行的方法。可以通过下面的代码测试这个类:
ThreadPoolExecutor executor = new ThreadPoolExecutor(6, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
for (int i = 0; i < 100; i++) {
executor.execute(new ParameterizedThread<>(i, (p) -> {
System.out.println(p.toString());
}));
}
Java带参数的线程类ParameterizedThread——即如何给Thread传递参数的更多相关文章
- Java 给Thread传递参数
一开始我想把run()函数写成有参函数来传值,后来发现行不通.经过查找,最终用如下方法传递了参数: 也就是用另外一个有参函数setTar()传递参数. 调用的时候用这4行code传递参数: 上面是用i ...
- java核心-多线程(4)-线程类基础知识
1.并发 <1>使用并发的一个重要原因是提高执行效率.由于I/O等情况阻塞,单个任务并不能充分利用CPU时间.所以在单处理器的机器上也应该使用并发. <2>为了实现并发,操作系 ...
- java 带静态域的导出类创建时都发生了什么?
先按从基类到导出类的顺序初始化静态域(之前已经初始化过的静态域不再初始化) 再按从基类到导出类的顺序初始化类,即基类普通字段+基类构造器主体+导出类字段+导出类主体... package test; ...
- ASP.net button类控件click事件中传递参数
单击Button会同时触发这两个事件,但先执行Click,后执行Command,在button控件中加上参数属性 CommandArgument='' 在click响应函数中可以用以下代码获得传递的参 ...
- [ 转载 ] Java基础14--创建线程的两个方法
http://www.cnblogs.com/whgw/archive/2011/10/03/2198506.html Java提供了线程类Thread来创建多线程的程序.其实,创建线程与创建普通的类 ...
- Java 基础 多线程和线程池基础
一,多线程 1.1 多线程介绍 进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线程:线程是进程中的一个执行单元,负 ...
- Java多线程学习(二)---线程创建方式
线程创建方式 摘要: 1. 通过继承Thread类来创建并启动多线程的方式 2. 通过实现Runnable接口来创建并启动线程的方式 3. 通过实现Callable接口来创建并启动线程的方式 4. 总 ...
- 关于Java中进程和线程的详解
一.进程:是程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程,是一个动态的实体,它有自己的生命 周期.它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而 ...
- JAVA学习笔记16——线程的创建和启动
Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例.每个线程的作用是完成一定的任务,实际上就是执行一段程序流(一段顺序执行的代码).Java使用线程执行体来代表这段 ...
随机推荐
- 账号被盗!请勿在CSDN,回复不论什么消息。
账号被盗!请勿在CSDN,回复不论什么消息.
- Android多线程研究(9)——读写锁
一.什么是锁 在Java的util.concurrent.locks包下有关于锁的接口和类如下: 先看一段代码: package com.codeing.snail.test; public clas ...
- MySQL启动关闭添加到 /etc/init.d/mysqld
cp /data/mysql/support-files/mysql.server /etc/init.d/mysqld 然后就可以使用此命令启动/关闭 mysql: /etc/ini ...
- 一个例子讲解wav头文件 stm32声音程序 录音和播放 wav
下面我们一wav头文件来分析一下: 下面是双声道的,16位,48000采样录的wav文件: 打开属性,能看到的有用信息只有比特率了: 上图的比特率就是 wav头文件里的bitrate: 1536kbp ...
- 【例题5-3 UVA - 10815】Andy's First Dictionary
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 用stringstream来处理中间的标点. ->直接把他变成一个空格. 然后重新输入进去. set默认的字典序就是升序的了. ...
- SharedPreferences基础 分类: H1_ANDROID 2013-11-04 22:35 2559人阅读 评论(0) 收藏
见归档项目:SharedPreferencesDemo.zip 1.对于数据量较小,且有明显的K-V形式的数据而言,适合用SharedPreferences保存.SharedPreferences的数 ...
- Linux中LVM2原理及制作LVM2
Linux中LVM2原理及制作LVM2 一.LVM原理 [MD]:Multi Device 多设备 Mdadm是一个用户空间工具,是RAID的管理工具,与真正的RAID工作没有太大关系.真正的RAID ...
- ubuntu里面设置颜色
比方我是这么写的:base_color:#FAF9DE;selected_bg_color:#008522 (背景为浅黄色,选择框为绿色) 写完之后按回车确认,立即生效! 这里另一个极其严重的 bug ...
- [ES6] Use ES6 Proxies
A JavaScript Proxy allows you to intercept operations performed on objects, arrays, or functions lik ...
- Android系统编译环境初始化时Product产品的import-nodes过程
从运行make -f config,mk文件開始,config,mk作为当前的makefile文件.将会被make解析,一般make解析Makefile文件流程首先是载入当中include的各种其它m ...