Làm cách nào tôi có thể truyền tham số cho Chủ đề Java?


290

Bất cứ ai có thể đề nghị với tôi làm thế nào tôi có thể truyền một tham số cho một chủ đề?

Ngoài ra, làm thế nào nó hoạt động cho các lớp ẩn danh?


5
Bạn có phiền để thêm một số giải thích thêm về chính xác mà bạn đang cố gắng làm? Có rất nhiều kỹ thuật cho nó, nhưng tất cả chúng đều phụ thuộc vào mục tiêu cuối cùng.
Artem Barger

4
Bạn có nghĩa là truyền một tham số cho một luồng đã chạy? Bởi vì tất cả các câu trả lời hiện tại là về việc truyền tham số cho chủ đề mới ...
Valentin Rocher

Bây giờ bạn có thể sử dụng Consumer<T>.
Alex78191

Câu trả lời:


371

Bạn cần truyền tham số trong hàm tạo cho đối tượng Runnable:

public class MyRunnable implements Runnable {

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

   public void run() {
   }
}

và gọi nó như vậy:

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

7
@jasonm không, đó là một nhà xây dựng - các nhà xây dựng không có kiểu trả về.
Alnitak

Điều này có nghĩa là mỗi Chủ đề được xây dựng bằng cách sử dụng rsẽ có cùng một đối số, vì vậy nếu chúng ta muốn chuyển các đối số khác nhau cho nhiều Chủ đề đang chạy, MyThreadchúng ta cần tạo một phiên bản mới MyThreadbằng cách sử dụng đối số mong muốn cho mỗi Chủ đề. Nói cách khác, để có được một luồng và chạy, chúng ta cần tạo hai đối tượng: Thread và MyThread. Đây có được coi là xấu, hiệu suất khôn ngoan?
Isaac Kleinman

2
@IsaacKleinman tốt, dù sao bạn cũng phải làm điều đó, trừ khi bạn mở rộng Thread. Và giải pháp này vẫn hoạt động trong trường hợp đó - chỉ cần thay đổi "thực hiện Runnable" thành "extends Thread", "Runnable" thành "Thread" và "new Thread (r)" thành "r".
dùng253751

111

Đối với các lớp ẩn danh:

Để trả lời các chỉnh sửa câu hỏi ở đây là cách nó hoạt động cho các lớp Ẩn danh

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

Các lớp được đặt tên:

Bạn có một lớp mở rộng Thread (hoặc thực hiện Runnable) và hàm tạo với các tham số bạn muốn truyền. Sau đó, khi bạn tạo chủ đề mới, bạn phải truyền vào các đối số và sau đó bắt đầu chủ đề, đại loại như thế này:

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

Runnable là một giải pháp tốt hơn nhiều so với Thread BTW. Vì vậy, tôi thích:

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

Câu trả lời này về cơ bản giống như câu hỏi tương tự này: Cách truyền tham số cho đối tượng Thread


2
Tôi có mã tương tự như ví dụ về lớp ẩn danh của bạn, ngoại trừ tôi truy cập parametertrực tiếp từ run()phương thức mà không sử dụng một trường nào pcả. Nó dường như làm việc. Có một số điều đa luồng tinh tế mà tôi đang thiếu bằng cách không sao chép parametervào ptrước?
Randall Cook

Tôi nghĩ rằng bạn đang thiếu một )ví dụ đầu tiên
Hack-R

Tôi đã có trải nghiệm tương tự như @RandallCook cho lớp Ẩn danh. Miễn là tôi có dòng final X parametertrước new Runnable(), sau đó tôi có thể truy cập parametervào bên trong run(). Tôi không cần phải làm thêm p = parameter.
wvducky

finalkhông còn quan trọng nữa; đủ để biến cuối cùng có hiệu quả (mặc dù nó không có hại gì)
user85421

43

thông qua hàm tạo của lớp Runnable hoặc 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 để hiển thị cách thực hiện việc mở rộng Chủ đề thay vì triển khai Runnable.
Caelum

1
Tại sao chúng ta cần @Overridecho?
Tuyết

@Snow @Overridenói rõ rằng nó đang ghi đè phương thức trừu tượng trong lớp Thread.
wyskoj

22

Câu trả lời này đến rất muộn, nhưng có lẽ ai đó sẽ thấy nó hữu ích. Đó là về cách truyền (các) tham số cho một Runnablemà thậm chí không khai báo lớp có tên (tiện dụng cho inliners):

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

Tất nhiên bạn có thể hoãn thực hiện startđến một số thời điểm thuận tiện hoặc thích hợp hơn. Và tùy thuộc vào bạn, đâu sẽ là chữ ký của initphương thức (vì vậy nó có thể mất nhiều hơn và / hoặc các đối số khác nhau) và tất nhiên ngay cả tên của nó, nhưng về cơ bản, bạn có một ý tưởng.

Trong thực tế, cũng có một cách khác để truyền tham số cho một lớp ẩn danh, với việc sử dụng các khối khởi tạo. Xem xét điều này:

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

Vì vậy, tất cả xảy ra bên trong khối khởi tạo.


Tôi thực sự thích ví dụ cuối cùng này khá tốt. Nhắc nhở tôi về việc sử dụng một bao đóng trong JS để tham chiếu các biến trong phạm vi bên ngoài. Nhưng this.myParamsau đó có thực sự cần thiết? Bạn không thể bỏ các biến riêng tư và tham khảo biến từ phạm vi bên ngoài? Tôi hiểu (tất nhiên) rằng điều này có một số hàm ý, chẳng hạn như biến được mở để thay đổi sau khi bắt đầu chuỗi.
oligofren

@JeffG thực sự đã trả lời điều này trong câu trả lời dưới đây!
oligofren

17

Khi bạn tạo một chủ đề, bạn cần một thể hiện của Runnable. Cách dễ nhất để truyền tham số sẽ là truyền tham số đó dưới dạng đối số cho hàm tạo:

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

Nếu sau đó bạn muốn thay đổi tham số trong khi luồng đang chạy, bạn có thể chỉ cần thêm một phương thức setter vào lớp có thể chạy của bạn:

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

Khi bạn có thứ này, bạn có thể thay đổi giá trị của tham số bằng cách gọi như thế này:

myRunnable.setMyParam("Goodbye World");

Tất nhiên, nếu bạn muốn kích hoạt một hành động khi thay đổi tham số, bạn sẽ phải sử dụng các khóa, điều này làm cho mọi thứ phức tạp hơn đáng kể.


1
Không thêm một setter tạo điều kiện cuộc đua tiềm năng? Nếu luồng bắt đầu với biến là một giá trị nhưng setter thay đổi thì việc thực thi giữa chừng đó có phải là vấn đề không?
anon58192932

Đúng, setter và tất cả quyền truy cập vào tham số nên được đồng bộ hóa.
jwoolard

9

Để tạo một chủ đề, bạn thường tạo triển khai Runnable của riêng bạn. Truyền tham số cho luồng trong hàm tạo của lớp này.

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

Bạn có thể mở rộng hoặc cung cấp các tham số theo ý muốn. Có những ví dụ đơn giản trong các tài liệu . Tôi sẽ chuyển chúng ở đây: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

Viết một lớp thực hiện Runnable và chuyển bất cứ thứ gì bạn cần vào một hàm tạo được xác định phù hợp hoặc viết một lớp mở rộng Thread bằng một hàm tạo được xác định phù hợp gọi super () với các tham số thích hợp.


6

Kể từ Java 8, bạn có thể sử dụng lambda để nắm bắt các tham số cuối cùng có hiệu quả . Ví dụ:

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

Trong Java 8, bạn có thể sử dụng các lambdabiểu thức với API đồng thời & ExecutorServicedưới dạng thay thế cấp cao hơn để làm việc trực tiếp với các luồng:

newCachedThreadPool()Tạo một nhóm luồng tạo ra các luồng mới khi cần, nhưng sẽ sử dụng lại các luồng được xây dựng trước đó khi chúng có sẵn. Các nhóm này thường sẽ cải thiện hiệu suất của các chương trình thực thi nhiều tác vụ không đồng bộ trong thời gian ngắn.

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

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

Xem thêm executors javadocs .


Cuối cùng là một cách để làm điều đó mà không có những lĩnh vực gây phiền nhiễu. Bây giờ tôi chỉ cần đợi sản phẩm của mình nâng cấp lên Java 8.
Sridhar Sarnobat

5

Tôi biết rằng tôi trễ vài năm, nhưng tôi đã gặp phải vấn đề này và đã thực hiện một cách tiếp cận không chính thống. Tôi muốn làm điều đó mà không cần tạo một lớp mới, vì vậy đây là những gì tôi nghĩ ra:

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

Tham số truyền qua các phương thức start () và 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

Bạn có thể lấy được một lớp từ Runnable và trong quá trình xây dựng (giả sử) truyền tham số vào.

Sau đó khởi chạy nó bằng Thread.start (Runnable r);

Nếu bạn có nghĩa là trong khi luồng đang chạy, thì chỉ cần giữ một tham chiếu đến đối tượng dẫn xuất của bạn trong luồng gọi và gọi các phương thức setter thích hợp (đồng bộ hóa khi thích hợp)


2

Có một cách đơn giản để truyền tham số vào runnables. Mã số:

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

2

Không, bạn không thể truyền tham số cho run()phương thức. Chữ ký cho bạn biết rằng (nó không có tham số). Có lẽ cách dễ nhất để làm điều này là sử dụng một đối tượng được xây dựng có mục đích lấy tham số trong hàm tạo và lưu trữ nó trong một biến cuối cùng:

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

Khi bạn làm điều đó - bạn phải cẩn thận về tính toàn vẹn dữ liệu của đối tượng bạn chuyển vào 'WorkTask'. Bây giờ dữ liệu sẽ tồn tại trong hai luồng khác nhau, do đó bạn phải chắc chắn rằng đó là Thread Safe.


1

Một lựa chọn nữa; Cách tiếp cận này cho phép bạn sử dụng mục Runnable như một lệnh gọi hàm không đồng bộ. Nếu nhiệm vụ của bạn không cần trả về kết quả, ví dụ: nó chỉ thực hiện một số hành động bạn không cần lo lắng về cách bạn trả lại "kết quả".

Mẫu này cho phép bạn sử dụng lại một mục, trong đó bạn cần một số loại trạng thái nội bộ. Khi không truyền (các) tham số trong chăm sóc hàm tạo là cần thiết để làm trung gian truy cập chương trình vào tham số. Bạn có thể cần kiểm tra thêm nếu trường hợp sử dụng của bạn liên quan đến những người gọi khác nhau, v.v.

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

  }

}

Chủ đề t = new Chủ đề (mới MyRunnable (tham số)); t.start ();

Nếu bạn cần kết quả xử lý, bạn cũng sẽ cần phối hợp hoàn thành MyRunnable khi tác vụ phụ kết thúc. Bạn có thể chuyển cuộc gọi lại hoặc chỉ chờ trong Chủ đề 't', v.v.


1

Đặc biệt dành cho Android

Đối với mục đích gọi lại, tôi thường triển khai chung chung Runnablevới (các) tham số đầu vào:

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

Cách sử dụng rất đơn giản:

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

Trong quản lý:

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

Tạo một biến cục bộ trong lớp của bạn đó extends Threadhoặc 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;
    }}

Bằng cách này, bạn có thể vượt qua một biến khi bạn chạy nó.

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

Đầu ra:

"www.google.com"
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.