パラメータをJavaスレッドに渡すにはどうすればよいですか?


290

誰かがスレッドにパラメーターを渡す方法を私に提案できますか?

また、匿名クラスではどのように機能しますか?


5
あなたがやろうとしていることについて、説明を追加していただけませんか?それにはいくつかのテクニックがたくさんありますが、それらはすべて最終目標に依存しています。
Artem Barger、

4
すでに実行中のスレッドにパラメーターを渡すということですか?現在の答えはすべて、新しいスレッドにパラメーターを渡すことについてです...
Valentin Rocher、

これでを使用できますConsumer<T>
Alex78191

回答:


371

コンストラクタのパラメータをRunnableオブジェクトに渡す必要があります。

public class MyRunnable implements Runnable {

   public MyRunnable(Object parameter) {
       // store parameter for later user
   }

   public void run() {
   }
}

そしてそれを呼び出す:

Runnable r = new MyRunnable(param_value);
new Thread(r).start();

7
@jasonmいいえ、それはコンストラクタです-コンストラクタには戻り値の型がありません。
Alnitak

つまり、使用しrて構築された各スレッドは同じ引数を持つため、実行中の複数のスレッドに異なる引数を渡したい場合は、各スレッドに必要な引数を使用してMyThread新しいMyThreadインスタンスを作成する必要があります。つまり、スレッドを起動して実行するには、ThreadとMyThreadという2つのオブジェクトを作成する必要があります。これはパフォーマンス面で悪いと考えられますか?
Isaac Kleinman 2013年

2
@IsaacKleinmanよく、スレッドを拡張しない限り、とにかくそうする必要があります。そしてこのソリューションはその場合でも機能します-「implements Runnable」を「extends Thread」に、「Runnable」を「Thread」に、そして「new Thread(r)」を「r」に変更するだけです。
user253751

111

匿名クラスの場合:

質問の編集に応じて、匿名のクラスでどのように機能するかを示します。

   final X parameter = ...; // the final is important
   Thread t = new Thread(new Runnable() {
       p = parameter;
       public void run() { 
         ...
       };
   t.start();

名前付きクラス:

Threadを拡張する(またはRunnableを実装する)クラスと、渡すパラメーターを持つコンストラクターがあります。次に、新しいスレッドを作成するときに、引数を渡してから、次のようなスレッドを開始する必要があります。

Thread t = new MyThread(args...);
t.start();

Runnableは、スレッドBTWよりもはるかに優れたソリューションです。だから私は好む:

   public class MyRunnable implements Runnable {
      private X parameter;
      public MyRunnable(X parameter) {
         this.parameter = parameter;
      }

      public void run() {
      }
   }
   Thread t = new Thread(new MyRunnable(parameter));
   t.start();

この回答は、基本的にはこの同様の質問と同じです:Threadオブジェクトにパラメーターを渡す方法


2
私はあなたの匿名クラスの例と同じようなコードを持っていますが、まったくフィールドを使用せずparameterrun()メソッドから直接アクセスpしています。動作しているようです。事前にコピーparameterしないことで見逃してしまう、微妙なマルチスレッド処理はありpますか?
Randall Cook

)最初の例であなたは欠けていると思います
Hack-R

私は匿名クラスの@RandallCookと同じ経験をしました。final X parameter前のnew Runnable()行がある限り、parameter内部にアクセスできましたrun()。余分なことをする必要はありませんでしたp = parameter
wisbucky、

finalもうそれほど重要ではありません。変数が事実上最終的なものである場合は十分です(それが害を及ぼすことはありません)
user85421

43

RunnableまたはThreadクラスのコンストラクターを介して

class MyThread extends Thread {

    private String to;

    public MyThread(String to) {
        this.to = to;
    }

    @Override
    public void run() {
        System.out.println("hello " + to);
    }
}

public static void main(String[] args) {
    new MyThread("world!").start();
}

5
+1は、Runnableを実装する代わりにThreadを拡張する方法を示します。
Caelum

1
なぜ必要なの@Overrideですか?

@Snowは@Override、Threadクラスの抽象メソッドをオーバーライドしていることを明示的に述べています。
wyskoj

22

この答えは非常に遅くなりますが、多分誰かがそれを役に立つでしょう。これは、Runnable名前付きクラスを宣言することなくパラメーターをに渡す方法についてです(インライナーに便利です)。

String someValue = "Just a demo, really...";
new Thread(new Runnable() {
    private String myParam;
    public Runnable init(String myParam) {
        this.myParam = myParam;
        return this;
    }
    @Override
    public void run() {
        System.out.println("This is called from another thread.");
        System.out.println(this.myParam);
    }
}.init(someValue)).start();

もちろん、実行をstartより便利で適切な瞬間に延期することができます。そして、initメソッドのシグネチャを何にするか(したがって、引数の数が増える可能性があります)、そしてもちろんその名前さえもあなた次第ですが、基本的にはアイデアを得ることができます。

実際、初期化ブロックを使用して、匿名クラスにパラメーターを渡す別の方法もあります。このことを考慮:

String someValue = "Another demo, no serious thing...";
int anotherValue = 42;

new Thread(new Runnable() {
    private String myParam;
    private int myOtherParam;
    {
        this.myParam = someValue;
        this.myOtherParam = anotherValue;
    }
    @Override
    public void run() {
        System.out.println("This comes from another thread.");
        System.out.println(this.myParam + ", " + this.myOtherParam);
    }
}).start();

したがって、すべて初期化ブロック内で発生します。


私は実際、この最後の例をかなり気に入っています。JSでクロージャーを使用して外部スコープの変数を参照することを思い出します。しかし、this.myParamそのとき本当に必要なのでしょうか?プライベート変数を削除して、外部スコープから変数を参照できませんでしたか?(もちろん)スレッドの開始後に変数を変更できるように開いているなど、これにはいくつかの影響があることを理解しています。
オリゴフレン2018

@JeffGは実際に以下の答えでこれに答えました!
オリゴフレン2018

17

スレッドを作成するときは、のインスタンスが必要ですRunnable。パラメータを渡す最も簡単な方法は、引数としてコンストラクタに渡すことです。

public class MyRunnable implements Runnable {

    private volatile String myParam;

    public MyRunnable(String myParam){
        this.myParam = myParam;
        ...
    }

    public void run(){
        // do something with myParam here
        ...
    }

}

MyRunnable myRunnable = new myRunnable("Hello World");
new Thread(myRunnable).start();

スレッドの実行中にパラメーターを変更する場合は、実行可能クラスにセッターメソッドを追加するだけです。

public void setMyParam(String value){
    this.myParam = value;
}

これを取得したら、次のように呼び出してパラメーターの値を変更できます。

myRunnable.setMyParam("Goodbye World");

もちろん、パラメーターが変更されたときにアクションをトリガーしたい場合は、ロックを使用する必要があるため、状況はかなり複雑になります。


1
セッターを追加すると、潜在的な競合状態が発生しませんか?スレッドが変数を1つの値として開始するが、セッターが実行中に変数を変更する場合、問題はありませんか?
anon58192932

True、セッターとパラメーターへのすべてのアクセスを同期する必要があります。
jwoolard 2017

9

スレッドを作成するには、通常、Runnableの独自の実装を作成します。このクラスのコンストラクターでスレッドにパラメーターを渡します。

class MyThread implements Runnable{
   private int a;
   private String b;
   private double c;

   public MyThread(int a, String b, double c){
      this.a = a;
      this.b = b;
      this.c = c;
   }

   public void run(){
      doSomething(a, b, c);
   }
}

8

またはのいずれかを拡張し、必要に応じてパラメーターを指定できます。docsに簡単な例があります。ここに移植します:Thread classRunnable class

 class PrimeThread extends Thread {
     long minPrime;
     PrimeThread(long minPrime) {
         this.minPrime = minPrime;
     }

     public void run() {
         // compute primes larger than minPrime
          . . .
     }
 }

 PrimeThread p = new PrimeThread(143);
 p.start();

 class PrimeRun implements Runnable {
     long minPrime;
     PrimeRun(long minPrime) {
         this.minPrime = minPrime;
     }

     public void run() {
         // compute primes larger than minPrime
          . . .
     }
 }


 PrimeRun p = new PrimeRun(143);
 new Thread(p).start();

7

Runnableを実装するクラスを作成し、適切に定義されたコンストラクターで必要なものを渡すか、適切なパラメーターでsuper()を呼び出す適切に定義されたコンストラクターでThreadを拡張するクラスを作成します。


6

Java 8以降では、ラムダを使用して、事実上最終的なパラメーターをキャプチャできます。例えば:

final String param1 = "First param";
final int param2 = 2;
new Thread(() -> {
    // Do whatever you want here: param1 and param2 are in-scope!
    System.out.println(param1);
    System.out.println(param2);
}).start();

5

Javaの8では、あなたは使用することができるlambdaとの表現を並行処理APIExecutorService直接スレッドを扱うためのより高いレベルの代替として:

newCachedThreadPool()必要に応じて新しいスレッドを作成するスレッドプールを作成しますが、以前に構築されたスレッドが利用可能な場合は再利用します。これらのプールは、通常、短期間の非同期タスクを多数実行するプログラムのパフォーマンスを向上させます。

    private static final ExecutorService executor = Executors.newCachedThreadPool();

    executor.submit(() -> {
        myFunction(myParam1, myParam2);
    });

executors javadocsも参照してください。


最後に、それらの迷惑なフィールドなしでそれを行う方法。今、私はただのJava 8にアップグレードするために、私の製品を待つ必要がある
シュリダールSarnobat

5

私は数年遅れていることを知っていますが、この問題に出くわし、正統でないアプローチをとりました。私は新しいクラスを作らずにそれをやりたかったので、これは私が思いついたものです:

int x = 0;
new Thread((new Runnable() {
     int x;
     public void run() {
        // stuff with x and whatever else you want
     }
     public Runnable pass(int x) {
           this.x = x;
           return this;
     }
}).pass(x)).start();

4

start()およびrun()メソッドを介して渡されるパラメーター:

// Tester
public static void main(String... args) throws Exception {
    ThreadType2 t = new ThreadType2(new RunnableType2(){
        public void run(Object object) {
            System.out.println("Parameter="+object);
        }});
    t.start("the parameter");
}

// New class 1 of 2
public class ThreadType2 {
    final private Thread thread;
    private Object objectIn = null;
    ThreadType2(final RunnableType2 runnableType2) {
        thread = new Thread(new Runnable() {
            public void run() {
                runnableType2.run(objectIn);
            }});
    }
    public void start(final Object object) {
        this.objectIn = object;
        thread.start();
    }
    // If you want to do things like setDaemon(true); 
    public Thread getThread() {
        return thread;
    }
}

// New class 2 of 2
public interface RunnableType2 {
    public void run(Object object);
}

3

Runnableからクラスを派生させ、構築中に(たとえば)パラメータを渡すことができます。

次に、Thread.start(Runnable r);を使用して起動します。

あなたが意味する場合は一方でスレッドが実行され、その後、単に呼び出しスレッドであなたの派生オブジェクトへの参照を保持し、(適切な場合に同期する)適切なsetterメソッドを呼び出します


2

ランナブルにパラメーターを渡す簡単な方法があります。コード:

public void Function(final type variable) {
    Runnable runnable = new Runnable() {
        public void run() {
            //Code adding here...
        }
    };
    new Thread(runnable).start();
}

2

いいえ、run()メソッドにパラメーターを渡すことはできません。シグネチャはそれを通知します(パラメーターはありません)。おそらくこれを行う最も簡単な方法は、コンストラクターでパラメーターを受け取り、それを最終変数に格納する専用オブジェクトを使用することです。

public class WorkingTask implements Runnable
{
    private final Object toWorkWith;

    public WorkingTask(Object workOnMe)
    {
        toWorkWith = workOnMe;
    }

    public void run()
    {
        //do work
    }
}

//...
Thread t = new Thread(new WorkingTask(theData));
t.start();

これを実行したら、「WorkingTask」に渡すオブジェクトのデータ整合性に注意する必要があります。データは2つの異なるスレッドに存在するようになるので、スレッドセーフであることを確認する必要があります。


1

さらに1つのオプション。このアプローチでは、非同期関数呼び出しのようにRunnableアイテムを使用できます。タスクが結果を返す必要がない場合、たとえば、「結果」を返す方法について心配する必要がないアクションを実行するだけです。

このパターンを使用すると、何らかの内部状態が必要なアイテムを再利用できます。コンストラクターでパラメーターを渡さない場合は、プログラムがパラメーターにアクセスできるように注意する必要があります。ユースケースに異なる呼び出し元が含まれる場合などは、さらにチェックが必要になる場合があります。

public class MyRunnable implements Runnable 
{
  private final Boolean PARAMETER_LOCK  = false;
  private X parameter;

  public MyRunnable(X parameter) {
     this.parameter = parameter;
  }

  public void setParameter( final X newParameter ){

      boolean done = false;
      synchronize( PARAMETER_LOCK )
      {
          if( null == parameter )
          {
              parameter = newParameter;
              done = true;
          }
      }
      if( ! done )
      {
          throw new RuntimeException("MyRunnable - Parameter not cleared." );
      }
  }


  public void clearParameter(){

      synchronize( PARAMETER_LOCK )
      {
          parameter = null;
      }
  }


  public void run() {

      X localParameter;

      synchronize( PARAMETER_LOCK )
      {
          localParameter = parameter;
      }

      if( null != localParameter )
      {
         clearParameter();   //-- could clear now, or later, or not at all ...
         doSomeStuff( localParameter );
      }

  }

}

スレッドt = new Thread(new MyRunnable(parameter)); t.start();

処理の結果が必要な場合は、サブタスクが終了したときにMyRunnableの完了を調整する必要もあります。コールバックを渡したり、スレッド「t」などで待機したりできます。


1

Android専用

コールバックのために、私は通常Runnable、入力パラメーターを使用して独自のジェネリックを実装します。

public interface Runnable<TResult> {
    void run(TResult result);
}

使い方は簡単です:

myManager.doCallbackOperation(new Runnable<MyResult>() {
    @Override
    public void run(MyResult result) {
        // do something with the result
    }
});

マネージャー:

public void doCallbackOperation(Runnable<MyResult> runnable) {
    new AsyncTask<Void, Void, MyResult>() {
        @Override
        protected MyResult doInBackground(Void... params) {
            // do background operation
            return new MyResult(); // return resulting object
        }

        @Override
        protected void onPostExecute(MyResult result) {
            // execute runnable passing the result when operation has finished
            runnable.run(result);
        }
    }.execute();
}

1

extends Threadまたはでクラスにローカル変数を作成しますimplements Runnable

public class Extractor extends Thread {
    public String webpage = "";
    public Extractor(String w){
        webpage = w;
    }
    public void setWebpage(String l){
        webpage = l;
    }

    @Override
    public void run() {// l is link
        System.out.println(webpage);
    }
    public String toString(){
        return "Page: "+webpage;
    }}

このようにして、実行時に変数を渡すことができます。

Extractor e = new Extractor("www.google.com");
e.start();

出力:

"www.google.com"
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.