Bagaimana saya bisa meneruskan parameter ke Java Thread?

290

Adakah yang bisa menyarankan saya bagaimana saya bisa mengirimkan parameter ke utas?

Juga, bagaimana cara kerjanya untuk kelas anonim?

Steve
sumber
5
Maukah Anda menambahkan penjelasan tambahan tentang hal itu persis seperti yang Anda coba lakukan? Ada banyak teknik untuk itu, tetapi semuanya tergantung pada tujuan akhir.
Artem Barger
4
Apakah maksud Anda meneruskan parameter ke utas yang sudah berjalan? Karena semua jawaban saat ini adalah tentang meneruskan parameter ke utas baru ...
Valentin Rocher
Sekarang kamu bisa menggunakannya Consumer<T>.
Alex78191

Jawaban:

371

Anda perlu meneruskan parameter dalam konstruktor ke objek Runnable:

public class MyRunnable implements Runnable {

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

   public void run() {
   }
}

dan memohon demikian:

Runnable r = new MyRunnable(param_value);
new Thread(r).start();
Alnitak
sumber
7
@ jasonm tidak, ini adalah konstruktor - konstruktor tidak memiliki tipe pengembalian.
Alnitak
Ini berarti bahwa setiap Thread yang dibangun menggunakan rakan memiliki argumen yang sama, jadi jika kita ingin memberikan argumen berbeda ke beberapa Thread yang sedang berjalan, MyThreadkita perlu membuat MyThreadinstance baru menggunakan argumen yang diinginkan untuk setiap Thread. Dengan kata lain, untuk mendapatkan thread dan menjalankan kita perlu membuat dua objek: Thread dan MyThread. Apakah ini dianggap buruk, bijaksana?
Isaac Kleinman
2
@IsaacKleinman juga, Anda harus tetap melakukannya, kecuali jika Anda memperpanjang Thread. Dan solusi ini masih berfungsi dalam kasus itu - ubah saja "implementasikan Runnable" menjadi "extends Thread", "Runnable" menjadi "Thread", dan "Thread baru (r)" ke "r".
user253751
111

Untuk kelas Anonim:

Menanggapi suntingan pertanyaan di sini adalah cara kerjanya untuk kelas Anonim

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

Kelas yang diberi nama:

Anda memiliki kelas yang memperluas Thread (atau mengimplementasikan Runnable) dan konstruktor dengan parameter yang ingin Anda sampaikan. Kemudian, ketika Anda membuat utas baru, Anda harus memberikan argumen, dan kemudian memulai utas, sesuatu seperti ini:

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

Runnable adalah solusi yang jauh lebih baik daripada Thread BTW. Jadi saya lebih suka:

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

Jawaban ini pada dasarnya sama dengan pertanyaan serupa ini: Cara meneruskan parameter ke objek Thread

Nick Fortescue
sumber
2
Saya memiliki kode yang mirip dengan contoh kelas anonim Anda, kecuali saya mengakses parameterlangsung dari run()metode tanpa menggunakan bidang seperti psama sekali. Sepertinya berhasil. Apakah ada beberapa hal multithreading halus yang saya lewatkan dengan tidak menyalin parameterke psebelumnya?
Randall Cook
Saya pikir Anda kehilangan )dalam contoh pertama
Hack-R
Saya memiliki pengalaman yang sama dengan @RandallCook untuk kelas Anonim. Selama saya punya final X parametersebelum new Runnable()baris, maka saya bisa mengakses parameterdi dalam run(). Saya tidak perlu melakukan ekstra p = parameter.
wisbucky
finaltidak begitu penting lagi; itu sudah cukup jika variabel efektif final (meskipun tidak ada salahnya)
user85421
43

melalui konstruktor dari kelas Runnable atau 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();
}
dfa
sumber
5
+1 untuk menunjukkan cara melakukannya memperluas Thread alih-alih menerapkan Runnable.
Caelum
1
mengapa kita membutuhkannya @Override?
Salju
@Sekarang @Overridesecara eksplisit menyatakan bahwa itu menimpa metode abstrak di kelas Thread.
wyskoj
22

Jawaban ini datang sangat terlambat, tetapi mungkin seseorang akan menemukannya berguna. Ini adalah tentang cara meneruskan parameter ke kelas Runnabletanpa mendeklarasikan kelas bernama (berguna untuk inliner):

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

Tentu saja Anda dapat menunda eksekusi startke momen yang lebih nyaman atau tepat. Dan terserah Anda apa yang akan menjadi tanda tangan initmetode (sehingga mungkin diperlukan lebih banyak dan / atau argumen yang berbeda) dan tentu saja bahkan namanya, tetapi pada dasarnya Anda mendapatkan ide.

Bahkan ada juga cara lain untuk mengirimkan parameter ke kelas anonim, dengan menggunakan blok initializer. Pertimbangkan ini:

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

Jadi semua terjadi di dalam blok initializer.

Cromax
sumber
Saya sebenarnya menyukai contoh terakhir ini dengan cukup baik. Mengingatkan saya untuk menggunakan penutupan di JS untuk referensi variabel di lingkup luar. Tetapi apakah this.myParamitu benar-benar diperlukan? Tidak bisakah Anda hanya menjatuhkan variabel pribadi dan merujuk ke variabel dari lingkup luar? Saya mengerti (tentu saja) bahwa ini memiliki beberapa implikasi, seperti variabel yang terbuka untuk diubah setelah memulai utas.
oligofren
@ JeffFG sebenarnya menjawab ini dalam jawaban di bawah ini!
oligofren
17

Saat Anda membuat utas, Anda membutuhkan instance dari Runnable. Cara termudah untuk mengirimkan parameter adalah dengan mengirimkannya sebagai argumen ke konstruktor:

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

Jika kemudian Anda ingin mengubah parameter saat utas berjalan, Anda bisa menambahkan metode setter ke kelas runnable Anda:

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

Setelah ini, Anda dapat mengubah nilai parameter dengan memanggil seperti ini:

myRunnable.setMyParam("Goodbye World");

Tentu saja, jika Anda ingin memicu aksi ketika parameter diubah, Anda harus menggunakan kunci, yang membuat segalanya menjadi lebih kompleks.

joolool
sumber
1
Tidak menambahkan setter menciptakan kondisi lomba potensial? Jika utas dimulai dengan variabel sebagai satu nilai tetapi setter mengubahnya pertengahan-eksekusi bukankah itu bermasalah?
anon58192932
Benar, penyetel dan semua akses ke parameter harus disinkronkan.
jwoolard
9

Untuk membuat utas, Anda biasanya membuat implementasi Runnable Anda sendiri. Lewati parameter ke utas di konstruktor kelas ini.

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);
   }
}
Mnementh
sumber
8

Anda dapat memperpanjang atau dan menyediakan parameter yang Anda inginkan. Ada contoh sederhana dalam dokumen . Saya akan port mereka di sini: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();
bruno conde
sumber
7

Baik tulis kelas yang mengimplementasikan Runnable, dan berikan apa pun yang Anda butuhkan di konstruktor yang didefinisikan dengan tepat, atau tulis kelas yang memperluas Thread dengan konstruktor yang didefinisikan dengan tepat yang memanggil super () dengan parameter yang sesuai.

PaulJWilliams
sumber
6

Pada Java 8, Anda dapat menggunakan lambda untuk menangkap parameter yang final secara efektif . Sebagai contoh:

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();
Jeff G
sumber
5

Di Java 8 Anda dapat menggunakan lambdaekspresi dengan Concurrency API & ExecutorServicesebagai pengganti tingkat yang lebih tinggi untuk bekerja dengan utas secara langsung:

newCachedThreadPool()Membuat kumpulan utas yang membuat utas baru sesuai kebutuhan, tetapi akan menggunakan ulang utas yang sebelumnya dibuat saat tersedia. Kumpulan ini biasanya akan meningkatkan kinerja program yang menjalankan banyak tugas asinkron yang berumur pendek.

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

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

Lihat juga executors javadocs .

Stuart Cardall
sumber
Akhirnya cara untuk melakukannya tanpa bidang-bidang yang mengganggu. Sekarang saya hanya perlu menunggu produk saya di-upgrade ke Java 8.
Sridhar Sarnobat
5

Saya tahu bahwa saya terlambat beberapa tahun, tetapi saya menemukan masalah ini dan mengambil pendekatan yang tidak ortodoks. Saya ingin melakukannya tanpa membuat kelas baru, jadi inilah yang saya buat:

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();
meyi
sumber
4

Melewati parameter melalui metode start () dan 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);
}
Java42
sumber
3

Anda bisa mendapatkan kelas dari Runnable, dan selama konstruksi (katakanlah) masukkan parameter.

Kemudian jalankan menggunakan Thread.start (Runnable r);

Jika Anda berarti sementara benang sedang berjalan, maka hanya memegang referensi ke objek Anda berasal di thread memanggil, dan memanggil metode setter yang tepat (sinkronisasi mana yang sesuai)

Brian Agnew
sumber
2

Ada cara sederhana untuk mengirimkan parameter ke runnables. Kode:

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

Tidak, Anda tidak dapat meneruskan parameter ke run()metode. Tanda tangan memberi tahu Anda (tidak memiliki parameter). Mungkin cara termudah untuk melakukan ini adalah dengan menggunakan objek yang dibangun dengan tujuan yang mengambil parameter dalam konstruktor dan menyimpannya dalam variabel akhir:

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

Setelah Anda melakukannya - Anda harus berhati-hati dengan integritas data objek yang Anda berikan ke 'WorkingTask'. Data sekarang akan ada di dua utas berbeda sehingga Anda harus memastikan itu adalah Utas Aman.

Mihir Patel
sumber
1

Satu opsi lebih lanjut; pendekatan ini memungkinkan Anda menggunakan item Runnable seperti panggilan fungsi tidak sinkron. Jika tugas Anda tidak perlu mengembalikan hasil, misalnya hanya melakukan beberapa tindakan Anda tidak perlu khawatir tentang bagaimana Anda mengembalikan "hasil".

Pola ini memungkinkan Anda menggunakan kembali suatu item, di mana Anda memerlukan semacam kondisi internal. Ketika tidak melewati parameter dalam perawatan konstruktor diperlukan untuk memediasi akses program ke parameter. Anda mungkin perlu lebih banyak memeriksa jika kasus penggunaan Anda melibatkan penelepon yang berbeda, dll.

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

  }

}

Utas t = Utas baru (MyRunnable baru (parameter)); t.start ();

Jika Anda membutuhkan hasil pemrosesan, Anda juga perlu mengoordinasikan penyelesaian MyRunnable ketika sub-tugas selesai. Anda bisa menjawab panggilan kembali atau hanya menunggu di Thread 't', dll.

akan
sumber
1

Khusus untuk Android

Untuk tujuan panggilan balik, saya biasanya menerapkan generik sendiri Runnabledengan parameter input:

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

Penggunaannya sederhana:

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

Dalam manajer:

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

Buat variabel lokal di kelas Anda yang extends Threadatau 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;
    }}

Dengan cara ini, Anda bisa melewatkan variabel saat Anda menjalankannya.

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

Hasil:

"www.google.com"
calvin k
sumber