Bir Java İş parçacığına nasıl parametre iletebilirim?


290

Herkes bana nasıl bir iş parçacığına bir parametre geçirebilir miyim önerebilir?

Ayrıca, anonim sınıflar için nasıl çalışır?


5
Tam olarak yapmaya çalıştığınıza dair ekstra bir açıklama eklemek ister misiniz? Bunun için birçok teknik var, ancak hepsi nihai hedefe bağlı.
Artem Barger

4
Zaten çalışan bir iş parçacığına bir parametre iletmek mi istiyorsunuz? Tüm mevcut cevaplar parametreleri yeni iş parçacıklarına
iletmekle ilgili

Şimdi kullanabilirsiniz Consumer<T>.
Alex78191

Yanıtlar:


371

Yapıcıdaki parametreyi Runnable nesnesine geçirmeniz gerekir:

public class MyRunnable implements Runnable {

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

   public void run() {
   }
}

ve bu şekilde çağırın:

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

7
@jasonm no, bir kurucu - kurucuların dönüş türü yok.
Alnitak

Bu, kullanılarak oluşturulan her bir Konu raynı argümana sahip olacağı anlamına gelir , bu nedenle birden fazla Konuya farklı argümanlar iletmek istiyorsak, her Konu için istenen argümanı kullanarak MyThreadyeni bir MyThreadörnek oluşturmamız gerekir . Başka bir deyişle, bir iş parçacığını çalıştırmak ve çalıştırmak için iki nesne oluşturmamız gerekir: Thread ve MyThread. Bu kötü, performans açısından mı değerlendiriliyor?
Isaac Kleinman

2
@IsaacKleinman iyi, Konu genişletmek sürece yine de bunu yapmak zorunda. Ve bu çözüm hala bu durumda çalışır - sadece "Runnable" ı uygular "Thread" ı genişletir, "Runnable" ı "Thread" ve "new Thread (r)" yi "r" olarak değiştirir.
user253751

111

Anonim sınıflar için:

Soru düzenlemelerine yanıt olarak, Anonim sınıflar için nasıl çalıştığı

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

İsimli sınıflar:

İş parçacığını genişleten (veya Runnable uygulayan) bir sınıfınız ve geçmek istediğiniz parametreleri içeren bir kurucunuz var. Sonra, yeni bir iş parçacığı oluşturduğunuzda, bağımsız değişkenleri iletmek ve sonra iş parçacığı, böyle bir şey başlatmak gerekir:

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

Runnable, Thread BTW'den çok daha iyi bir çözümdür. Bu yüzden tercih ederim:

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

Bu yanıt temel olarak şu benzer soru ile aynıdır: Parametreler bir Thread nesnesine nasıl aktarılır


2
Anonim sınıf örneğinize benzer bir kodum var, ancak hiç bir alan kullanmadan parameterdoğrudan run()yöntemden erişiyorum p. İşe yarıyor gibi görünüyor. Ben kopyalayarak vermeyerek Eksik bazı ince çoklu şey var mı parameterhiç pönceden?
Randall Cook

)İlk örnekte bir eksik olduğunu düşünüyorum
Hack-R

Anonymous sınıfı için @RandallCook ile aynı deneyimi yaşadım. Sürece ben vardı final X parameterönce new Runnable()çizgi, o zaman ben erişebilir parameteriçeride run(). Ekstra yapmam gerekmiyordu p = parameter.
wisbucky

finalartık o kadar önemli değil; değişken olması durumunda, yeterli etkin bir nihai (hiçbir zarar having o does rağmen)
user85421

43

Runnable veya Thread sınıfının kurucusu aracılığıyla

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
Nasıl çalıştırılacağını gösteren +1, Runnable uygulamak yerine Thread'ı genişletir.
Caelum

1
neden ihtiyacımız var @Override?
Kar

@ @OverrideAçık bir şekilde, Thread sınıfındaki soyut yöntemi geçersiz kıldığını açıkça belirtir.
wyskoj

22

Bu cevap çok geç geliyor, ama belki birisi faydalı bulacaktır. Bu, Runnableadlandırılmış sınıfı bile bildirmeden bir parametreye / parametrelere nasıl aktarılacağıyla ilgilidir (satır içi için kullanışlı):

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

Tabii ki uygulanmasını startdaha uygun veya uygun bir an için erteleyebilirsiniz . Ve inityöntemin imzası ne olacaktır (bu yüzden daha fazla ve / veya farklı argümanlar alabilir) ve elbette onun adı bile, ama temelde bir fikir elde edersiniz.

Aslında, başlangıç ​​parametrelerini kullanarak bir parametreyi anonim bir sınıfa geçirmenin başka bir yolu da vardır. Bunu düşün:

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

Böylece her şey başlatıcı bloğunun içinde olur.


Aslında bu son örneği çok beğendim. Dış kapsamdaki değişkenlere başvurmak için JS'de bir kapatma kullanmamı hatırlatıyor. Ama o this.myParamzaman gerçekten gerekli mi? Özel değişkenleri bırakıp dış kapsamdaki değişkene atıfta bulunamaz mısınız? (Tabii ki) iş parçacığını başlattıktan sonra değişken değişime açık olması gibi bazı sonuçları olduğunu anlıyorum.
oligofren

@JeffG bunu aslında aşağıdaki cevapta yanıtladı!
oligofren

17

Bir iş parçacığı oluşturduğunuzda, bir örneğine ihtiyacınız vardır Runnable. Bir parametreyi iletmenin en kolay yolu, parametreyi yapıcıya bir argüman olarak iletmektir:

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

Daha sonra iş parçacığı çalışırken parametreyi değiştirmek isterseniz, runnable sınıfınıza bir ayarlayıcı yöntemi ekleyebilirsiniz:

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

Bunu yaptıktan sonra, şu şekilde çağırarak parametrenin değerini değiştirebilirsiniz:

myRunnable.setMyParam("Goodbye World");

Tabii ki, parametre değiştirildiğinde bir eylemi tetiklemek istiyorsanız, kilitleri kullanmanız gerekir, bu da işleri daha karmaşık hale getirir.


1
Bir ayarlayıcı eklemek potansiyel yarış koşulları yaratmıyor mu? İş parçacığı değişkenle bir değer olarak başlıyor, ancak ayarlayıcı bunu değiştirirse, yürütme ortasında sorunlu olmaz mı?
anon58192932

Doğru, ayarlayıcı ve parametreye tüm erişim senkronize edilmelidir.
jwoolard

9

Bir iş parçacığı oluşturmak için normalde kendi Runnable uygulamasını oluşturursunuz. Parametreleri bu sınıfın yapıcısındaki iş parçacığına iletin.

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

Ya uzatabilirsiniz veya ve istediğiniz kadar parametreleri sunar. Dokümanlarda basit örnekler var . Onları buraya taşıyacağım: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'ı uygulayan bir sınıf yazın ve ihtiyacınız olan her şeyi uygun şekilde tanımlanmış bir kurucuya iletin ya da Thread'ı uygun parametrelerle super () çağıran uygun tanımlı bir kurucu ile genişleten bir sınıf yazın.


6

Java 8'den itibaren, etkili olan son parametreleri yakalamak için bir lambda kullanabilirsiniz . Örneğin:

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'de kullanabileceğiniz lambdaile ifadeleri eşzamanlılık API & ExecutorServicedoğrudan parçacığı ile çalışmak için daha yüksek düzeyde yerine:

newCachedThreadPool()Gerektiğinde yeni iş parçacıkları oluşturan, ancak önceden oluşturulmuş iş parçacıklarını kullanılabilir olduğunda yeniden kullanacak bir iş parçacığı havuzu oluşturur. Bu havuzlar genellikle kısa ömürlü birçok zaman uyumsuz görevi yürüten programların performansını artıracaktır.

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

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

Ayrıca executors bkz . Javadocs .


Sonunda bu sinir bozucu alanlar olmadan yapmanın bir yolu. Şimdi sadece
ürünümün

5

Birkaç yıl geç kaldığımı biliyorum, ama bu konuyla karşılaştım ve alışılmadık bir yaklaşım izledim. Ben yeni bir sınıf yapmadan yapmak istedim, bu yüzden ben geldi budur:

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 () ve run () yöntemlerinden geçen parametre:

// 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'dan bir sınıf türetebilirsiniz ve inşaat sırasında (diyelim) parametreyi iletin.

Sonra Thread.start (Runnable r) kullanarak başlatın;

Eğer demek istediğin iken çalışan iş parçacığı, o zaman sadece çağıran iş parçacığı içinde türetilmiş bir nesneye bir başvuru tutun ve uygun ayarlayıcı yöntemleri çağırmak (senkronize uygun olduğunda)


2

Parametreleri çalıştırılabilir kablolara geçirmenin basit bir yolu vardır. Kod:

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

2

Hayır, parametreye run()yönteme geçemezsiniz . İmza size bunu söyler (parametresi yoktur). Muhtemelen bunu yapmanın en kolay yolu, yapıcıda bir parametre alan ve bir son değişkente saklayan amaca yönelik bir nesne kullanmak olacaktır:

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

Bunu yaptıktan sonra - 'WorkingTask'a ilettiğiniz nesnenin veri bütünlüğüne dikkat etmelisiniz. Veriler artık iki farklı iş parçacığında mevcut olacaktır, bu nedenle iş parçacığının Güvenli olduğundan emin olmanız gerekir.


1

Başka bir seçenek; bu yaklaşım Runnable öğesini eşzamansız işlev çağrısı gibi kullanmanızı sağlar. Görevinizin bir sonuç döndürmesi gerekmiyorsa, örneğin sadece bir "sonucu" nasıl geri verdiğiniz konusunda endişelenmenize gerek olmayan bazı eylemleri gerçekleştirir.

Bu desen, bir tür dahili duruma ihtiyaç duyduğunuz bir öğeyi yeniden kullanmanızı sağlar. Kurucuda parametre (ler) geçirilmediğinde programların parametrelere erişimine aracılık etmek için bakım gerekir. Kullanım durumunuz farklı arayanlar vb. İçeriyorsa daha fazla kontrole ihtiyacınız olabilir.

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

  }

}

Konu t = yeni Konu (yeni MyRunnable (parametre)); t.start ();

İşlemenin bir sonucuna ihtiyacınız varsa, alt görev tamamlandığında MyRunnable'ın tamamlanmasını da koordine etmeniz gerekir. Bir aramayı geri verebilir veya sadece 't', vb.


1

Özellikle Android için

Geri arama amaçları için genellikle kendi genel kodumu Runnablegirdi parametreleri ile uygularım :

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

Kullanımı basit:

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

Yöneticide:

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

Sınıfınızda yerel bir değişken oluşturun extends Threadveya 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;
    }}

Bu şekilde, bir değişkeni çalıştırdığınızda iletebilirsiniz.

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

Çıktı:

"www.google.com"
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.