在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传递参数的更多相关文章

  1. Java 给Thread传递参数

    一开始我想把run()函数写成有参函数来传值,后来发现行不通.经过查找,最终用如下方法传递了参数: 也就是用另外一个有参函数setTar()传递参数. 调用的时候用这4行code传递参数: 上面是用i ...

  2. java核心-多线程(4)-线程类基础知识

    1.并发 <1>使用并发的一个重要原因是提高执行效率.由于I/O等情况阻塞,单个任务并不能充分利用CPU时间.所以在单处理器的机器上也应该使用并发. <2>为了实现并发,操作系 ...

  3. java 带静态域的导出类创建时都发生了什么?

    先按从基类到导出类的顺序初始化静态域(之前已经初始化过的静态域不再初始化) 再按从基类到导出类的顺序初始化类,即基类普通字段+基类构造器主体+导出类字段+导出类主体... package test; ...

  4. ASP.net button类控件click事件中传递参数

    单击Button会同时触发这两个事件,但先执行Click,后执行Command,在button控件中加上参数属性 CommandArgument='' 在click响应函数中可以用以下代码获得传递的参 ...

  5. [ 转载 ] Java基础14--创建线程的两个方法

    http://www.cnblogs.com/whgw/archive/2011/10/03/2198506.html Java提供了线程类Thread来创建多线程的程序.其实,创建线程与创建普通的类 ...

  6. Java 基础 多线程和线程池基础

    一,多线程 1.1 多线程介绍 进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线程:线程是进程中的一个执行单元,负 ...

  7. Java多线程学习(二)---线程创建方式

    线程创建方式 摘要: 1. 通过继承Thread类来创建并启动多线程的方式 2. 通过实现Runnable接口来创建并启动线程的方式 3. 通过实现Callable接口来创建并启动线程的方式 4. 总 ...

  8. 关于Java中进程和线程的详解

    一.进程:是程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程,是一个动态的实体,它有自己的生命 周期.它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而 ...

  9. JAVA学习笔记16——线程的创建和启动

    Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例.每个线程的作用是完成一定的任务,实际上就是执行一段程序流(一段顺序执行的代码).Java使用线程执行体来代表这段 ...

随机推荐

  1. Rick's RoTs -- Rules of Thumb for MySQL--转载

    原文地址:http://mysql.rjweb.org/doc.php/ricksrots Brought to you by Rick James Here are 160+ tips, trick ...

  2. Notepad++和MinGW的安装和配置

    http://blog.csdn.net/cclovepl/article/details/70568313 http://blog.csdn.net/cclovepl/article/details ...

  3. report_timing_requirement

    report_timing_requirement   -ignored 会报告set_faults_paths,set_multi_path等

  4. 重构——DataTable转泛型

         泛型简单介绍         泛型能够最大限度的重用代码.保护类型的安全.提高性能.         泛型最常见的用途是创建集合类         泛型数据类型中使用的信息可在执行时通过反射 ...

  5. [AngularFire 2] Push, remove, update

    import { Injectable } from '@angular/core'; import {RealtimeService} from "../shared"; imp ...

  6. VC和MATLAB混合开发经验总结

    作者:朱金灿 来源:http://blog.csdn.net/clever101 前期准备: 1.请确认机器中已经安装Matlab主程序或(MCR)MATLAB Compiler Runtime(具体 ...

  7. AndroidAnnotations使用说明书—AndroidAnnotations是怎样工作的?

    AndroidAnnotations的工作方式非常easy.它使用标准的java注入处理工具,自己主动加入了一个额外的编译步骤来生成源码. 源代码是什么?每个增强的类,比方每个用@EActivity注 ...

  8. [AngualrJS NG-redux] Map State and Dispatchers to Redux

    In this lesson, we are going to learn how to map our Angular component directly to our application s ...

  9. Spring boot(三) springboot 定时任务

    这个不多说,springboot 定时任务非常简单就可以实现了. 30s运行一次 , @Scheduled(cron="0,30 * * * * ?") 通过这个控制定时时间 cr ...

  10. 【27.91%】【codeforces 734E】Anton and Tree

    time limit per test3 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...