【问题标题】:Runnable with a parameter?可以带参数运行吗?
【发布时间】:2011-08-16 17:26:00
【问题描述】:

我需要一个“接受参数的 Runnable”,尽管我知道这样的 runnable 并不存在。

这可能表明我的应用程序设计存在根本缺陷和/或我疲倦的大脑中存在心理障碍,所以我希望在这里找到一些关于如何完成类似以下操作的建议,没有 em> 违反了基本的 OO 原则:

  private Runnable mOneShotTask = new Runnable(String str) {
    public void run(String str) {
       someFunc(str);
    }
  };  

知道如何完成上述操作吗?

【问题讨论】:

  • 现在您可以使用Consumer<T>
  • 我已经阅读了这个问题的各种答案。让我感到奇怪的是,没有人告诉您可以将所需的 Runnables 添加到您的项目中(带有一个、两个、三个或更多参数),只需添加一个适当的接口即可。我在这里为感兴趣的人创建了一个评论要点:gist.github.com/jsfan3/3a66e711fd0fd233c5e4c467184adb7a
  • 这不是“如何将参数传递给 Java 线程”的副本。现代的答案是,就像@Alex78191 所说:使用Consumer<T>

标签: java runnable


【解决方案1】:

自从我最初发布这篇文章以来已经快 9 年了,老实说,从那时起,Java 已经进行了一些改进。我将在下面留下我原来的答案,但人们不需要做其中的事情。 9 年前,在代码审查期间,我会质疑他们为什么这样做,也许批准了,也许没有。有了现代 lambda 表达式,有如此高票数的答案推荐过时的方法是不负责任的(公平地说,一开始是可疑的......)在现代 Java 中,代码审查将立即被拒绝,这将是建议:

void foo(final String str) {
    Thread t = new Thread(() -> someFunc(str));
    t.start();
}

和以前一样,以有意义的方式处理该线程之类的细节留给读者作为练习。但说白了,如果你害怕使用 lambdas,你应该更害怕多线程系统。

原来的答案,只是因为:

你可以在方法中声明一个类

void Foo(String str) {
    class OneShotTask implements Runnable {
        String str;
        OneShotTask(String s) { str = s; }
        public void run() {
            someFunc(str);
        }
    }
    Thread t = new Thread(new OneShotTask(str));
    t.start();
}

【讨论】:

  • 谢谢大家!所有建议的解决方案都指向相同的方法,但我只能接受一种。我一定很累,不能自己想出这个。向所有人 +1。
  • 实际上,大多数人不知道你可以在方法中声明一个类。有些人会认为这是一种糟糕的风格。我想这是一个品味问题。 :)
  • 公共接口 ResultRunnable { public void run(T result); }
【解决方案2】:

你有两个选择:

  1. 定义一个命名类。将参数传递给命名类的构造函数。

  2. 让你的匿名类关闭你的“参数”。请务必将其标记为final

【讨论】:

【解决方案3】:

我首先想知道你在这里想要完成什么,需要一个参数传递给 new Runnable() 或 run()。 通常的方法应该是有一个 Runnable 对象,它通过在开始之前设置成员变量将数据(str)传递给它的线程。 run() 方法然后使用这些成员变量值来执行 someFunc()

【讨论】:

    【解决方案4】:

    你可以把它放在一个函数中。

    String paramStr = "a parameter";
    Runnable myRunnable = createRunnable(paramStr);
    
    private Runnable createRunnable(final String paramStr){
    
        Runnable aRunnable = new Runnable(){
            public void run(){
                someFunc(paramStr);
            }
        };
    
        return aRunnable;
    
    }
    

    (当我使用它时,我的参数是一个整数 ID,我用它来制作 ID 的哈希图 --> myRunnables。这样,我可以使用哈希图在处理程序中发布/删除不同的 myRunnable 对象。)

    【讨论】:

    • 感谢分享代码 - 我喜欢人们这样做而不是喋喋不休。一个问题 - 在内存泄漏方面,上述方法是否可行?您传递的所有参考资料都会被妥善处理?
    • @kape123 答案是“视情况而定”。只要该方法返回的Runnable 对象存在于任何地方,paramStr 就可能不符合垃圾回收条件。如果对象存在但永远无法再次运行,JIT(甚至 javac)可能会决定将其从作用域中删除,但我们不应依赖此类优化,因为它们将来可能会发生变化。
    • “感谢分享代码 - 我喜欢人们这样做,而不仅仅是喋喋不休。” - 什么?
    • @RobGrant 真正具有讽刺意味的是,在那之后,他们又特地要求说废话!!哦,他们只想要他们需要的确切的喋喋不休?明白了。
    【解决方案5】:
    theView.post(new Runnable() {
        String str;
        @Override                            
        public void run() {
            par.Log(str);                              
        }
        public Runnable init(String pstr) {
            this.str=pstr;
            return(this);
        }
    }.init(str));
    

    创建返回对象本身并用它初始化参数的init函数。

    【讨论】:

    • 不幸的是,您将无法将其分配给变量并调用 init()
    【解决方案6】:

    我使用以下实现Runnable 接口的类。使用这个类,您可以轻松地创建带有参数的新线程

    public abstract class RunnableArg implements Runnable {
    
        Object[] m_args;
    
        public RunnableArg() {
        }
    
        public void run(Object... args) {
            setArgs(args);
            run();
        }
    
        public void setArgs(Object... args) {
            m_args = args;
        }
    
        public int getArgCount() {
            return m_args == null ? 0 : m_args.length;
        }
    
        public Object[] getArgs() {
            return m_args;
        }
    }
    

    【讨论】:

    • 你将如何使用这个抽象类来运行一个带参数的可运行对象?
    • 我更喜欢使用带参数的构造函数public RunnableArg(Object... args) { setArgs(args); } 然后描述本地类:class ActionArg extends RunnableArg { public ActionArg(Object... arg) { super(arg); } @Override public void run() { /* getArgs() and process them */ } } 并使用它Thread t = new Thread(new ActionArg( %param_object(s)% )); t.start();
    【解决方案7】:

    从 Java 8 开始,最好的答案是使用Consumer<T>

    https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html

    它是函数式接口之一,这意味着您可以将其称为 lambda 表达式:

    void doSomething(Consumer<String> something) {
        something.accept("hello!");
    }
    
    ...
    
    doSomething( (something) -> System.out.println(something) )
    
    ...
    

    【讨论】:

      【解决方案8】:
      /**
       * @author AbdelWadoud Rasmi
       * <p>
       * The goal of this class is to pass some parameters to a runnable instance, a good example is
       * after caching a file you need to pass the new path to user to do some work on it.
       */
      public abstract class ParameterizedRunnable implements Runnable {
          private Object[] params;
      
          /**
           * @param params: parameters you want to pass the the runnable.
           */
          public ParameterizedRunnable(Object... params) {
              this.params = params;
          }
      
          /**
           * Code you want to run
           *
           * @param params:parameters you want to pass the the runnable.
           */
          protected abstract void run(Object... params);
      
          @Override
          public final void run() {
              run(params);
          }
      
          /**
           * setting params
           */
          public void setParams(Object... params) {
              this.params = params;
          }
      
          /**
           * getting params
           */
          public Object[] getParams() {
              return params;
          }
      }
      

      【讨论】:

      • 这种方式是最实用也是最简单的方式,只需要创建一个类继承Runnable带参数(getters/setters)和另外一个run方法,原来重写的run方法调用你定义的方法(保存参数的方法)
      • 漂亮干净。
      【解决方案9】:

      目前最好的方法:

      Consumer<String> oneShot = str -> {
          
         somefunc(str);
          
      };
      
      oneShot.accept("myString");
      

      【讨论】:

        猜你喜欢
        • 2020-02-09
        • 2013-06-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-03
        • 1970-01-01
        • 1970-01-01
        • 2014-01-10
        相关资源
        最近更新 更多