ฉันจะส่งพารามิเตอร์ไปยัง Java Thread ได้อย่างไร


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 สิ่งนี้ถือว่าแย่ประสิทธิภาพฉลาดหรือไม่?
Isaac Kleinman

2
@IsaacKleinman ดีคุณต้องทำเช่นนั้นเว้นแต่คุณจะขยาย Thread และวิธีการแก้ปัญหานี้ยังคงใช้งานได้ในกรณีนี้ - เพียงแค่เปลี่ยน "ใช้ Runnable" เป็น "ขยายเธรด", "Runnable" เป็น "Thread" และ "new Thread (r)" เป็น "r"
253751

111

สำหรับคลาสที่ไม่ระบุชื่อ:

ในการตอบคำถามการแก้ไขที่นี่เป็นวิธีการทำงานสำหรับคลาสที่ไม่เปิดเผยตัวตน

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

คลาสที่มีชื่อ:

คุณมีคลาสที่ขยายเธรด (หรือเรียกใช้เรียกใช้) และตัวสร้างพร้อมพารามิเตอร์ที่คุณต้องการส่ง จากนั้นเมื่อคุณสร้างเธรดใหม่คุณต้องผ่านอาร์กิวเมนต์และเริ่มเธรดดังนี้:

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

Runnable เป็นทางออกที่ดีกว่า Thread 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
ฉันมีรหัสคล้ายกับตัวอย่างคลาสที่ไม่ระบุชื่อของคุณยกเว้นฉันเข้าถึงparameterได้โดยตรงจากrun()วิธีการโดยไม่ต้องใช้ฟิลด์อย่างpที่ทุกคน ดูเหมือนว่าจะทำงาน มีบางสิ่งบางอย่าง multithreading ที่ละเอียดอ่อนที่ฉันหายไปโดยไม่คัดลอกparameterไปpไว้ล่วงหน้าหรือไม่?
Randall Cook

ฉันคิดว่าคุณพลาด)ตัวอย่างแรกไปแล้ว
Hack-R

ฉันมีประสบการณ์เช่นเดียวกับ @RandallCook สำหรับชั้นไม่ระบุชื่อ ตราบใดที่ฉันมีบรรทัดfinal X parameterก่อนหน้าnew Runnable()จากนั้นฉันก็สามารถparameterเข้าไปข้างในrun()ได้ p = parameterฉันไม่จำเป็นต้องทำพิเศษ
wisbucky

finalไม่สำคัญอีกต่อไป; ก็พอถ้าตัวแปรสุดท้ายได้อย่างมีประสิทธิภาพ (แม้จะไม่เป็นอันตรายต่อมัน)
user85421

43

ผ่าน Constructor ของ Runnable หรือ Thread class

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
Caelum

1
ทำไมเราต้องใช้@Overrideเพื่อ?
หิมะตก

@Snow @Overrideระบุอย่างชัดเจนว่ามีการแทนที่เมธอด abstract ในคลาส Thread
wyskoj

22

คำตอบนี้มาช้ามาก แต่อาจมีบางคนคิดว่ามันมีประโยชน์ มันเกี่ยวกับวิธีการส่งพารามิเตอร์ไปยัง a Runnableโดยไม่ต้องประกาศ class ที่มีชื่อ

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วิธีการ (ดังนั้นจึงอาจใช้เวลามากขึ้นและ / หรือข้อโต้แย้งที่แตกต่างกัน) และแน่นอนแม้กระทั่งชื่อของมัน แต่โดยทั่วไปคุณจะได้รับความคิด

ในความเป็นจริงยังมีอีกวิธีหนึ่งในการส่งพารามิเตอร์ไปยังคลาสที่ไม่ระบุชื่อด้วยการใช้บล็อก initializer พิจารณาสิ่งนี้:

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

ดังนั้นทั้งหมดเกิดขึ้นภายในบล็อก initializer


ฉันชอบตัวอย่างสุดท้ายนี้ค่อนข้างดี เตือนให้ฉันใช้การปิดใน JS เพื่ออ้างอิงตัวแปรในขอบเขตด้านนอก แต่this.myParamจำเป็นจริงๆหรือ? คุณไม่สามารถวางตัวแปรส่วนตัวและอ้างอิงตัวแปรจากขอบเขตด้านนอกได้หรือไม่ ฉันเข้าใจ (แน่นอน) ว่าสิ่งนี้มีความหมายบางอย่างเช่นตัวแปรที่เปิดให้เปลี่ยนหลังจากเริ่มเธรด
oligofren

@JeffG ตอบคำถามนี้ตามคำตอบจริงด้านล่าง!
oligofren

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

หากคุณต้องการเปลี่ยนพารามิเตอร์ในขณะที่เธรดกำลังทำงานอยู่คุณสามารถเพิ่มเมธอด setter ให้กับคลาสที่รันได้ของคุณ:

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

เมื่อคุณมีสิ่งนี้คุณสามารถเปลี่ยนค่าของพารามิเตอร์ได้โดยการเรียกสิ่งนี้:

myRunnable.setMyParam("Goodbye World");

แน่นอนถ้าคุณต้องการทริกเกอร์การกระทำเมื่อมีการเปลี่ยนแปลงพารามิเตอร์คุณจะต้องใช้การล็อกซึ่งทำให้สิ่งต่าง ๆ มีความซับซ้อนมากขึ้น


1
ไม่ได้เพิ่มตัวตั้งค่าสร้างเงื่อนไขการแข่งขัน หากเธรดเริ่มต้นด้วยตัวแปรเป็นค่าเดียว แต่ตัวตั้งค่าเปลี่ยนค่าเป็นตัวประมวลผลกลางจะไม่เป็นปัญหาหรือไม่
anon58192932

True ตัวตั้งค่าและการเข้าถึงพารามิเตอร์ทั้งหมดควรซิงโครไนซ์
jwoolard

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

คุณสามารถขยายหรือหรือและให้พารามิเตอร์ตามที่คุณต้องการ มีตัวอย่างที่ง่ายในการที่มีเอกสาร ฉันจะย้ายพวกเขาที่นี่: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 และส่งผ่านสิ่งที่คุณต้องการใน Constructor ที่กำหนดไว้อย่างเหมาะสมหรือเขียนคลาสที่ขยาย Thread ด้วย Constructor ที่กำหนดไว้อย่างเหมาะสมซึ่งเรียก super () พร้อมพารามิเตอร์ที่เหมาะสม


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นิพจน์ด้วยConcurrency API & ExecutorServiceแทนระดับที่สูงขึ้นสำหรับการทำงานกับเธรดโดยตรง:

newCachedThreadPool()สร้างเธรดพูลที่สร้างเธรดใหม่ตามต้องการ แต่จะใช้เธรดที่สร้างขึ้นก่อนหน้านี้ใหม่เมื่อพร้อมใช้งาน กลุ่มเหล่านี้มักจะปรับปรุงประสิทธิภาพของโปรแกรมที่เรียกใช้งานแบบอะซิงโครนัสระยะสั้นจำนวนมาก

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

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

ดูเพิ่มเติมjavadocsexecutors


ในที่สุดวิธีที่จะทำโดยไม่มีเขตข้อมูลที่น่ารำคาญเหล่านั้น ตอนนี้ฉันแค่ต้องรอให้ผลิตภัณฑ์ของฉันอัปเกรดเป็น Java 8
Sridhar 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

มีวิธีง่ายๆในการส่งพารามิเตอร์ไปยัง runnables รหัส:

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

2

ไม่คุณไม่สามารถส่งพารามิเตอร์ไปยังrun()วิธีการได้ ลายเซ็นจะบอกคุณว่า (ไม่มีพารามิเตอร์) อาจเป็นวิธีที่ง่ายที่สุดในการทำเช่นนี้คือการใช้วัตถุที่สร้างขึ้นโดยมีวัตถุประสงค์เพื่อรับพารามิเตอร์ใน Constructor และเก็บไว้ในตัวแปรสุดท้าย:

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' ขณะนี้ข้อมูลจะมีอยู่ในสองเธรดที่แตกต่างกันดังนั้นคุณต้องแน่ใจว่าเป็นเธรดที่ปลอดภัย


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 = เธรดใหม่ (ใหม่ MyRunnable (พารามิเตอร์)); 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 Threadimplements 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.