2010-02-22 18 views
17

से मैं निम्नलिखित की तरह एक जावा धागा है:वापसी मान जावा धागे

public class MyThread extends Thread { 
     MyService service; 
     String id; 
     public MyThread(String id) { 
      this.id = node; 
     } 
     public void run() { 
      User user = service.getUser(id) 
     } 
    } 

मैं लगभग 300 आईडी है, और सेकंड के हर जोड़े को - मैं आईडी से प्रत्येक के लिए एक फोन करना धागे ऊपर आग। उदाहरण के लिए।

for(String id: ids) { 
    MyThread thread = new MyThread(id); 
    thread.start(); 
} 

अब, मैं बजाय बनाने 300 डेटाबेस हर 2 सेकंड सम्मिलित करता है की, प्रत्येक धागे से परिणाम एकत्रित करते हैं, और डेटाबेस के लिए एक बैच डालने करना चाहते हैं।

कोई विचार यह है कि मैं इसे कैसे पूरा कर सकता हूं?

उत्तर

17

आप डेटाबेस अद्यतन करने से पहले परिणाम के सभी इकट्ठा करने के लिए चाहते हैं, आप invokeAll विधि का उपयोग कर सकते हैं। यह बहीखाता का ख्याल रखता है, यदि आप एक समय में कार्यों को सबमिट करते हैं, जैसे कि daveb सुझाव देता है।

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

... 

Collection<Callable<User>> tasks = new ArrayList<Callable<User>>(); 
for (final String id : ids) { 
    tasks.add(new Callable<User>() 
    { 

    public User call() 
     throws Exception 
    { 
     return svc.getUser(id); 
    } 

    }); 
} 
/* invokeAll blocks until all service requests complete, 
* or a max of 10 seconds. */ 
List<Future<User>> results = workers.invokeAll(tasks, 10, TimeUnit.SECONDS); 
for (Future<User> f : results) { 
    User user = f.get(); 
    /* Add user to batch update. */ 
    ... 
} 
/* Commit batch. */ 
... 
+0

मेरे मामले में, कुछ सेवा कॉल कभी वापस नहीं आ सकती हैं या वापसी के लिए बहुत अधिक समय ले सकती हैं। तो 'इंतजार करें' के साथ 'awaitTermination (long timeout)' जाने के तरीके की तरह दिखता है। इसलिए मैं इस जवाब को स्वीकार कर रहा हूं। इच्छा है कि मैं @ डेव के जवाब भी स्वीकार कर सकता हूं। – Langali

+0

उस स्थिति में, आप 'invokeAll' के ओवरलोडेड संस्करण का उपयोग कर सकते हैं जो टाइमआउट पैरामीटर लेता है। मैं यह दिखाने के लिए अपना उत्तर अपडेट करूंगा कि कैसे। – erickson

+0

धन्यवाद। मैंने किसी भी तरह इसे अनदेखा किया। – Langali

34

कैननिकल दृष्टिकोण Callable और ExecutorService का उपयोग करना है। submitCallable को ExecutorService पर एक (टाइपएफ़) Future देता है जिससे आप get परिणाम प्राप्त कर सकते हैं।

class TaskAsCallable implements Callable<Result> { 
    @Override 
    public Result call() { 
     return a new Result() // this is where the work is done. 
    } 
} 

ExecutorService executor = Executors.newFixedThreadPool(300); 
Future<Result> task = executor.submit(new TaskAsCallable()); 
Result result = task.get(); // this blocks until result is ready 

आपके मामले में, आप शायद अपने आप को invokeAll जो Futures के List रिटर्न उपयोग करें, या कि सूची बनाने के लिए के रूप में आप निष्पादक को कार्य जोड़ना चाहते हैं। परिणाम एकत्र करने के लिए, बस प्रत्येक पर get पर कॉल करें।

+0

अच्छा पोस्ट, विस्तार के लिए +1 जोड़ सकते हैं जब सभी वायदा के लिए तैयार हैं,। – fastcodejava

1

आपको परिणाम को सिंगलटन जैसे कुछ स्टोर करने की आवश्यकता है। इसे ठीक से सिंक्रनाइज़ किया जाना है।

संपादित करें: मुझे यह सबसे अच्छी सलाह नहीं है क्योंकि कच्चे Threads को संभालना अच्छा नहीं है। लेकिन सवाल यह काम करेगा, है ना? मुझे उखाड़ फेंक नहीं दिया जा सकता है, लेकिन वोट क्यों नीचे?

1

आप एक कतार या सूची बना सकते हैं जो आप अपने द्वारा बनाए गए धागे को पास करते हैं, थ्रेड सूची में अपना परिणाम जोड़ते हैं जो बैच डालने वाले उपभोक्ता द्वारा खाली हो जाता है।

1

सबसे आसान तरीका प्रत्येक थ्रेड (एक थ्रेड प्रति ऑब्जेक्ट) में ऑब्जेक्ट को पास करना है जिसमें परिणाम बाद में होगा। मुख्य धागे को प्रत्येक परिणाम वस्तु का संदर्भ रखना चाहिए। जब सभी धागे जुड़ जाते हैं, तो आप परिणामों का उपयोग कर सकते हैं।

1
public class TopClass { 
    List<User> users = new ArrayList<User>(); 
    void addUser(User user) { 
     synchronized(users) { 
      users.add(user); 
     } 
    } 
    void store() throws SQLException { 
     //storing code goes here 
    } 
    class MyThread extends Thread { 
      MyService service; 
      String id; 
      public MyThread(String id) { 
       this.id = node; 
      } 
      public void run() { 
       User user = service.getUser(id) 
       addUser(user); 
      } 
     } 
} 
1

आप एक कक्षा बना सकते हैं जो पर्यवेक्षण को बढ़ाती है। फिर आपका धागा अवलोकन वर्ग में एक विधि को कॉल कर सकता है जो पर्यवेक्षक.notifyObservers (ऑब्जेक्ट) को कॉल करके उस पर्यवेक्षक में पंजीकृत किसी भी वर्ग को सूचित करेगा।

अवलोकन कक्षा पर्यवेक्षक को लागू करेगी, और खुद को पर्यवेक्षक के साथ पंजीकृत करेगी। इसके बाद आप एक अद्यतन (अवलोकन, ऑब्जेक्ट) विधि लागू करेंगे जिसे ऑब्जर्वरेबल.नोटिफ़ी ऑब्सर्वर (ऑब्जेक्ट) कहा जाता है।

4

अपना परिणाम अपने ऑब्जेक्ट में स्टोर करें। जब यह पूरा हो जाता है, तो इसे अपने आप को एक सिंक्रनाइज़ संग्रह में छोड़ दें (एक सिंक्रनाइज़ कतार दिमाग में आती है)।

जब आप सबमिट करने के लिए अपने परिणाम एकत्र करना चाहते हैं, तो कतार से सबकुछ लें और वस्तुओं से अपने परिणाम पढ़ें।आप प्रत्येक ऑब्जेक्ट को यह भी जानते हैं कि डेटाबेस के अपने परिणामों को "पोस्ट" कैसे करें, इस तरह अलग-अलग वर्ग सबमिट किए जा सकते हैं और सभी ठीक उसी छोटे, सुरुचिपूर्ण पाश के साथ संभाले जा सकते हैं।

इसके साथ मदद करने के लिए जेडीके में बहुत से टूल्स हैं, लेकिन एक बार जब आप अपने धागे को एक वास्तविक वस्तु के रूप में सोचना शुरू करते हैं और न केवल "रन" विधि के आसपास बकवास का एक गुच्छा शुरू करना आसान होता है। एक बार जब आप ऑब्जेक्ट्स के बारे में सोचना शुरू कर देते हैं तो प्रोग्रामिंग बहुत आसान और अधिक संतोषजनक हो जाती है।

+0

+1। मेरा प्रारंभिक विचार समान था, लेकिन जावा 5 ने इसे इतना आसान बना दिया! – Langali

+0

+1। वह सुंदर है। –

+0

क्या यह एक ऐसी प्रणाली के लिए किया गया था जिसने कक्षा बी पता श्रेणी को पिंग किया था। यह बहुत ही सुरुचिपूर्ण था और समय से कुछ मिनटों तक काट दिया। (ठीक है, एक पिंग से एक एसएनएमपी में भी बदल गया, जावा वास्तव में पिंग का समर्थन नहीं करता है) –

2

जावा 8 में CompletableFuture का उपयोग करके ऐसा करने का बेहतर तरीका है।

static class GenerateNumber implements Supplier<Integer>{ 

    private final int number; 

    GenerateNumber(int number){ 
     this.number = number; 
    } 
    @Override 
    public Integer get() { 
     try { 
      TimeUnit.SECONDS.sleep(1); 
     }catch (InterruptedException e){ 
      e.printStackTrace(); 
     } 
     return this.number; 
    } 
} 

अब हम एक समवर्ती संग्रह के लिए परिणाम जोड़ सकते हैं एक बार हर भविष्य के परिणाम के लिए तैयार है, हम वर्ग है कि डेटाबेस से की आईडी मिलता है, सादगी के लिए हम सिर्फ नीचे के रूप में एक नंबर लौट सकते है कहो।

Collection<Integer> results = new ConcurrentLinkedQueue<>(); 
int tasks = 10; 
CompletableFuture<?>[] allFutures = new CompletableFuture[tasks]; 
for (int i = 0; i < tasks; i++) { 
    int temp = i; 
    CompletableFuture<Integer> future = CompletableFuture.supplyAsync(()-> new GenerateNumber(temp).get(), executor); 
    allFutures[i] = future.thenAccept(results::add); 
} 

अब हम एक कॉलबैक

CompletableFuture.allOf(allFutures).thenAccept(c->{ 
    System.out.println(results); // do something with result 
}); 
संबंधित मुद्दे