2012-02-05 36 views
60

Tôi có phương thức với số HandlerThread. Giá trị được thay đổi bên trong Thread và tôi muốn trả lại giá trị đó theo phương thức test(). Có cách nào để làm việc này không?Trả lại giá trị từ Chủ đề

public void test() 
{ 
    Thread uiThread = new HandlerThread("UIHandler"){ 
     public synchronized void run(){ 
      int value; 
      value = 2; //To be returned to test() 
     } 
    }; 
    uiThread.start(); 
} 
+3

Ngôn ngữ này là gì? –

+0

Nếu chủ đề chính phải đợi cho đến khi bộ xử lý kết thúc trước khi trở về từ phương pháp, tại sao lại sử dụng một bộ xử lý ở vị trí đầu tiên? –

+0

@JBNizet Tôi không bao gồm sự phức tạp của những gì Chủ đề thực sự làm. Nó nhận được tọa độ gps vì vậy có tôi cần một chủ đề. – Neeta

Trả lời

47

Bạn có thể sử dụng mảng biến cục bộ cuối cùng. Biến cần phải có kiểu không nguyên thủy, vì vậy bạn có thể sử dụng một mảng. Bạn cũng cần phải đồng bộ hóa hai chủ đề, ví dụ sử dụng một CountDownLatch:

public void test() 
{ 
    final CountDownLatch latch = new CountDownLatch(1); 
    final int[] value = new int[1]; 
    Thread uiThread = new HandlerThread("UIHandler"){ 
     @Override 
     public void run(){ 
      value[0] = 2; 
      latch.countDown(); // Release await() in the test thread. 
     } 
    }; 
    uiThread.start(); 
    latch.await(); // Wait for countDown() in the UI thread. Or could uiThread.join(); 
    // value[0] holds 2 at this point. 
} 

Bạn cũng có thể sử dụng một ExecutorCallable như thế này:

public void test() throws InterruptedException, ExecutionException 
{ 
    ExecutorService executor = Executors.newSingleThreadExecutor(); 
    Callable<Integer> callable = new Callable<Integer>() { 
     @Override 
     public Integer call() { 
      return 2; 
     } 
    }; 
    Future<Integer> future = executor.submit(callable); 
    // future.get() returns 2 or raises an exception if the thread dies, so safer 
    executor.shutdown(); 
} 
+1

Um ... không. Mã này không chính xác. Quyền truy cập vào giá trị không được đồng bộ hóa chính xác. –

+3

Chúng tôi không thực sự cần đồng bộ hóa rõ ràng cho các truy cập đến giá trị do sự đảm bảo tính nhất quán của bộ nhớ của CountDownLatch. Việc tạo mảng giá trị xảy ra trước khi bắt đầu uiThread (quy tắc thứ tự chương trình) đồng bộ hóa - với việc gán giá trị 2 cho giá trị [0] ([thread start] (http://docs.oracle.com/javase/specs/jls/se7 /html/jls-17.html#jls-17.4.4)) xảy ra trước khi latch.countDown() (quy tắc thứ tự chương trình) xảy ra trước khi latch.await() (đảm bảo từ CountDownLatch) xảy ra trước khi đọc từ giá trị [0] (quy tắc thứ tự chương trình). –

+0

Có vẻ như bạn đã đúng về Latch! ... trong trường hợp đó, việc đồng bộ hóa phương thức chạy là vô ích. –

48

Thông thường, bạn sẽ làm điều đó một cái gì đó như thế này

public class Foo implements Runnable { 
    private volatile int value; 

    @Override 
    public void run() { 
     value = 2; 
    } 

    public int getValue() { 
     return value; 
    } 
} 

Sau đó, bạn có thể tạo ra các chủ đề và lấy giá trị (cho rằng các giá trị đã được thiết lập)

Foo foo = new Foo(); 
new Thread(foo).start(); 
// ... join through some method 
int value = foo.getValue(); 

tl;dr một chuỗi không thể trả về giá trị (ít nhất là không có cơ chế gọi lại). Bạn nên tham khảo một chuỗi như một lớp bình thường và yêu cầu giá trị.

+2

Điều này thực sự có hiệu quả không? Tôi nhận 'Phương thức getValue() không được định nghĩa cho kiểu Thread'. – pmichna

+1

@pmichna, phát hiện tốt. Thay đổi từ 't.getValue()' thành 'foo.getValue()'. –

+0

@ JohanSjöberg: Tnx cho câu trả lời của bạn. nhưng có một lỗi nhỏ trong đó. bạn shoud gọi "chủ đề mới (foo) .start()"; – mostafa88

21

Điều bạn đang tìm kiếm có lẽ là giao diện Callable<V> thay cho Runnable và truy xuất giá trị với đối tượng Future<V>, cũng cho phép bạn đợi cho đến khi giá trị được tính. Bạn có thể đạt được điều này với một số ExecutorService, mà bạn có thể nhận được từ Executors.newSingleThreadExecutor().

public void test() { 
    int x; 
    ExecutorService es = Executors.newSingleThreadExecutor(); 
    Future<Integer> result = es.submit(new Callable<Integer>() { 
     public Integer call() throws Exception { 
      // the other thread 
      return 2; 
     } 
    }); 
    try { 
     x = result.get(); 
    } catch (Exception e) { 
     // failed 
    } 
    es.shutdown(); 
} 
4

Nếu bạn muốn giá trị từ phương pháp gọi điện thoại , sau đó nó sẽ chờ cho thread để kết thúc, mà làm cho việc sử dụng các chủ đề một chút vô nghĩa.

Để trả lời trực tiếp câu hỏi của bạn, giá trị có thể được lưu trữ trong bất kỳ đối tượng có thể thay đổi nào cả phương thức gọi và chuỗi đều có tham chiếu đến. Bạn có thể sử dụng bên ngoài this, nhưng điều đó sẽ không đặc biệt hữu ích ngoài các ví dụ tầm thường.

Một lưu ý nhỏ về mã trong câu hỏi: Mở rộng Thread thường là kiểu kém. Thực sự mở rộng các lớp không cần thiết là một ý tưởng tồi. Tôi nhận thấy bạn phương pháp run được đồng bộ hóa vì một số lý do. Bây giờ là đối tượng trong trường hợp này là Thread bạn có thể can thiệp vào bất cứ điều gì Thread sử dụng khóa của nó cho (trong việc thực hiện tham chiếu, một cái gì đó để làm với join, IIRC).

+0

"sau đó nó sẽ đợi cho đến khi chuỗi kết thúc, điều này làm cho việc sử dụng chuỗi chỉ là một chút vô nghĩa" điểm tuyệt vời! – likejiujitsu

+3

Thường thì vô nghĩa, nhưng trong Android, bạn không thể thực hiện yêu cầu mạng tới máy chủ trên Chủ đề chính (để giữ ứng dụng đáp ứng), do đó bạn sẽ phải sử dụng chuỗi mạng. Có các tình huống mà bạn cần kết quả trước khi ứng dụng có thể tiếp tục. –

5

Còn giải pháp này thì sao?

Nó không sử dụng lớp Thread, nhưng đó là đồng thời, và trong một cách thức mà nó thực hiện chính xác những gì bạn yêu cầu

ExecutorService pool = Executors.newFixedThreadPool(2); // creates a pool of threads for the Future to draw from 

Future<Integer> value = pool.submit(new Callable<Integer>() { 
    @Override 
    public Integer call() {return 2;} 
}); 

Bây giờ tất cả các bạn làm là nói value.get() bất cứ khi nào bạn cần phải lấy giá trị trả về của bạn , chủ đề được bắt đầu ngay sau khi bạn cung cấp cho value một giá trị để bạn không bao giờ phải nói threadName.start() trên đó.

Thật là một Future là, là một lời hứa cho chương trình, bạn hứa chương trình mà bạn sẽ nhận được nó giá trị nó cần lúc nào đó trong tương lai gần

Nếu bạn gọi .get() vào nó trước khi nó được thực hiện , chủ đề đang gọi nó sẽ chỉ đơn giản là đợi cho đến khi hoàn thành

+0

Tôi tách mã được cung cấp thành hai thứ một là bắt đầu hồ bơi mà tôi làm trong lớp Ứng dụng (tôi đang nói về android) và thứ hai tôi sử dụng hồ bơi ở những nơi tôi cần nó ... Cũng liên quan đến việc sử dụng Executors.newFixedThreadPool (2) tôi đã sử dụng Executors.newSingleThreadExecutor() .. vì tôi chỉ cần một nhiệm vụ chạy tại một thời điểm cho các cuộc gọi máy chủ ... Câu trả lời của bạn là hoàn hảo @electirc coffee thanks –

1

Sử dụng các câu trả lời ở trên được mô tả trong tương lai, nhưng ít hơn đáng kể như f.get(), chặn chuỗi cho đến khi kết quả vi phạm đồng thời.

Giải pháp tốt nhất là sử dụng ListenableFuture của Ổi. Ví dụ:

ListenableFuture<Void> future = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1, new NamedThreadFactory).submit(new Callable<Void>() 
    { 
     @Override 
     public Void call() throws Exception 
     { 
      someBackgroundTask(); 
     } 
    }); 
    Futures.addCallback(future, new FutureCallback<Long>() 
    { 
     @Override 
     public void onSuccess(Long result) 
     { 
      doSomething(); 
     } 

     @Override 
     public void onFailure(Throwable t) 
     { 

     } 
    }; 
0

Với những sửa đổi nhỏ cho mã của bạn, bạn có thể đạt được nó theo cách tổng quát hơn.

final Handler responseHandler = new Handler(Looper.getMainLooper()){ 
      @Override 
      public void handleMessage(Message msg) { 
       //txtView.setText((String) msg.obj); 
       Toast.makeText(MainActivity.this, 
         "Result from UIHandlerThread:"+(int)msg.obj, 
         Toast.LENGTH_LONG) 
         .show(); 
      } 
     }; 

     HandlerThread handlerThread = new HandlerThread("UIHandlerThread"){ 
      public void run(){ 
       Integer a = 2; 
       Message msg = new Message(); 
       msg.obj = a; 
       responseHandler.sendMessage(msg); 
       System.out.println(a); 
      } 
     }; 
     handlerThread.start(); 

Giải pháp:

  1. Tạo một Handler trong UI Chủ đề, được gọi như responseHandler
  2. Khởi Handler này từ Looper UI Chủ đề.
  3. Trong HandlerThread, bưu tin nhắn trên này responseHandler
  4. handleMessgae cho thấy một Toast với giá trị nhận được từ tin nhắn. Đối tượng Message này là chung và bạn có thể gửi các kiểu thuộc tính khác nhau.

Với phương pháp này, bạn có thể gửi nhiều giá trị cho chuỗi giao diện người dùng tại các thời điểm khác nhau. Bạn có thể chạy (post) nhiều đối tượng Runnable trên HandlerThread này và mỗi đối tượng Runnable có thể đặt giá trị trong đối tượng Message, có thể nhận được bởi Giao diện người dùng.

0

Java 8 cung cấp CompletableFuture. nó sẽ là giải pháp một cửa cho việc này. http://www.baeldung.com/java-completablefuture

+1

Điều này giống như một bình luận như trái ngược với câu trả lời. Bạn vui lòng nâng cấp nó bằng ví dụ (thậm chí sao chép và dán từ liên kết) để khi liên kết không hợp lệ, mọi người vẫn có thể tìm thấy câu trả lời của bạn hữu ích. –

Các vấn đề liên quan