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());
    }));
}

原文地址:https://www.cnblogs.com/wangnmhb/p/9226550.html

时间: 2024-10-07 18:08:33

Java带参数的线程类ParameterizedThread——即如何给Thread传递参数的相关文章

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

1.并发 <1>使用并发的一个重要原因是提高执行效率.由于I/O等情况阻塞,单个任务并不能充分利用CPU时间.所以在单处理器的机器上也应该使用并发. <2>为了实现并发,操作系统层面提供了.但是进程的数量和开销都有限制,并且多个进程之间的数据共享比较麻烦.另一种比较轻量的并发实现是使用线程,一个进程可以包含多个线程.线程在进程中没有数量限制, 数据共享相对简单.线程的支持跟语言是有关系的.Java 语言中支持多线程. <3>Java 中的多线程是抢占式的.这意味着一个任

boost thread 传递参数

#include <boost/thread/thread.hpp> #include <boost/bind.hpp> #include <iostream> void threadFunc(const char* pszContext) { std::cout << pszContext << std::endl; } int main(int argc, char* argv[]) { char* pszContext = "[e

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

http://www.cnblogs.com/whgw/archive/2011/10/03/2198506.html Java提供了线程类Thread来创建多线程的程序.其实,创建线程与创建普通的类的对象的操作是一样的,而线程就是Thread类或其子类的实例对象.每个Thread对象描述了一个单独的线程.要产生一个线程,有两种方法: ◆需要从Java.lang.Thread类派生一个新的线程类,重载它的run()方法: ◆实现Runnalbe接口,重载Runnalbe接口中的run()方法.

java eclipse 中给args 传递参数

我们都知道java源文件运行时通过main主函数为入口启动的,在cmd命令窗口我们知道怎么传递参数,但是在编译器里边就不知道怎么传递参数了, 在这里有一个简单的例子: public class Test{ public static void main(String[] args){ //这里的args就是传入的参数数组. }}在cmd中 java Test a b c 但是在eclipse中的话,对参数的传递就没有什么头绪了,这里可以在运行 选中项目主类右键在run as里选中 run con

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

一.进程:是程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程,是一个动态的实体,它有自己的生命 周期.它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而被撤消.反映了一个程序在 一定的数据 集上运行的全部动态过程.通过进程控制块(PCB)唯一的标识某个进程.同时进程占据着相应的资源(例如包 括cpu的使用 ,轮转时间以及一些其它设备的权限).是系统进行资源分配和调度的一个独立单位. 程序和进程之间的主要区别在于: 状态         是否具有资源

创建线程类threading.py

import threadingfrom time import sleep,ctime#创建线程类:class MyThread(threading.Thread): def __init__(self,func,args,name=""): threading.Thread.__init__(self) self.func = func self.args = args self.name = name def run(self): self.func(*self.args) de

cpp 线程传递参数

目录 给thread传递参数的 多种情况: 传递字面值 传递字符数组当字符串, 为了安全还是转换成 string 更好. 向线程传递一个引用, 注意需要使用 ref 函数 thread 传递函数对象,需要注意,如果在 thread 构造参数里面构造对象,需要用"()"包起来 仿照 bind 将对象的成员函数作为线程的参数 向thread传递指针:普通指针和智能指针, 需要注意安全 unique_ptr的movable语义传递参数, 需要使用 move 函数 给thread传递参数的 多

delphi的一些语法知识 以及参数传递问题,按引用方式传递参数,按值方式传递参数

//delphi中exit,abort,break,continue 的区别 exit: 退出函数体abort: 遇到异常,安静处理,就是不显示不提示break: 退出当前循环体,包括for ,while, repeat等循环体continue: 结束循环内的本次处理,继续从循环体的开始位置继续执行 Exit 是跳出当前代码块,也就是当前函数,跳出后是要继续向下执行的(如果有后续代码). Abort 是从 EAbort 过来的,可以激发 exception,其实质就是 Abort = Raise

Delphi过程、函数传递参数的八种方式

Delphi过程函数传递参数的八种方式 Delphi过程.函数传递参数的八种方式,布布扣,bubuko.com