জাভা এক্সিকিউটররা: কোনও কাজ শেষ হলে বাধা না দিয়ে কীভাবে বিজ্ঞপ্তি দেওয়া যায়?


154

বলুন যে আমার কাছে কার্যগুলি পূর্ণ একটি সারি রয়েছে যা আমাকে একজন নির্বাহক পরিষেবাতে জমা দিতে হবে। আমি তাদের একবারে প্রক্রিয়া করতে চাই। সবচেয়ে সহজ উপায় আমি ভাবতে পারি তা হ'ল:

  1. সারি থেকে একটি কাজ নিন
  2. এটি নির্বাহকের কাছে জমা দিন
  3. ফিরে আসা ভবিষ্যতে কল করুন। ফল এবং ফলাফল উপলব্ধ না হওয়া পর্যন্ত অবরুদ্ধ করুন
  4. সারি থেকে অন্য একটি কাজ নিন ...

তবে আমি সম্পূর্ণরূপে অবরুদ্ধ হওয়া এড়াতে চেষ্টা করছি। যদি আমার কাছে 10,000 টি সারি থাকে, যার কাজগুলি একবারে একবারে প্রক্রিয়াজাত করা প্রয়োজন তবে আমি স্ট্যাকের বাইরে চলে যাব কারণ তাদের বেশিরভাগই অবরুদ্ধ থ্রেড ধরে থাকবে।

আমি যা চাই তা হল একটি টাস্ক জমা দেওয়ার এবং একটি কল-ব্যাক সরবরাহ করা যা টাস্কটি শেষ হয়ে গেলে ডাকা হয়। আমি পরবর্তী কলটি পাঠাতে পতাকা হিসাবে সেই কল-ব্যাক বিজ্ঞপ্তিটি ব্যবহার করব। (ফাংশনালজভা এবং জেটল্যাং দৃশ্যত এ জাতীয় ব্লককারী অ্যালগরিদম ব্যবহার করে তবে আমি তাদের কোড বুঝতে পারি না)

আমি কীভাবে এটি করতে পারি জেডিকে এর জাভা.ইটিল.কমারেন্ট ব্যবহার করে, আমার নিজস্ব নির্বাহক পরিষেবা লেখার সংক্ষিপ্ত?

(এই ক্রিয়াগুলি আমাকে যে ক্রিয়াকলাপগুলি সরবরাহ করে সেগুলি নিজেই অবরুদ্ধ হতে পারে, তবে এটি পরবর্তীকালে মোকাবেলা করার বিষয়)

উত্তর:


146

সমাপ্তির বিজ্ঞপ্তিতে আপনি যে কোনও প্যারামিটারগুলি পাস করতে চান তা পেতে কলব্যাক ইন্টারফেসটি সংজ্ঞায়িত করুন। তারপরে টাস্ক শেষে এটিকে অনুরোধ করুন।

আপনি রান্নেবল টাস্কগুলির জন্য একটি সাধারণ মোড়ক লিখতেও পারেন এবং এগুলিতে জমা দিতে পারেন ExecutorService। অথবা, জাভা 8 এ নির্মিত কোনও প্রক্রিয়াটির জন্য নীচে দেখুন।

class CallbackTask implements Runnable {

  private final Runnable task;

  private final Callback callback;

  CallbackTask(Runnable task, Callback callback) {
    this.task = task;
    this.callback = callback;
  }

  public void run() {
    task.run();
    callback.complete();
  }

}

এর সাথে CompletableFuture, জাভা 8 এর মধ্যে পাইপলাইন রচনা করার জন্য আরও বিস্তৃত উপায় অন্তর্ভুক্ত করা হয়েছে যেখানে প্রক্রিয়াগুলি সংকীর্ণ ও শর্তসাপেক্ষে সম্পন্ন করা যায়। এখানে বিজ্ঞপ্তির একটি কনট্রিভড তবে সম্পূর্ণ উদাহরণ।

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

public class GetTaskNotificationWithoutBlocking {

  public static void main(String... argv) throws Exception {
    ExampleService svc = new ExampleService();
    GetTaskNotificationWithoutBlocking listener = new GetTaskNotificationWithoutBlocking();
    CompletableFuture<String> f = CompletableFuture.supplyAsync(svc::work);
    f.thenAccept(listener::notify);
    System.out.println("Exiting main()");
  }

  void notify(String msg) {
    System.out.println("Received message: " + msg);
  }

}

class ExampleService {

  String work() {
    sleep(7000, TimeUnit.MILLISECONDS); /* Pretend to be busy... */
    char[] str = new char[5];
    ThreadLocalRandom current = ThreadLocalRandom.current();
    for (int idx = 0; idx < str.length; ++idx)
      str[idx] = (char) ('A' + current.nextInt(26));
    String msg = new String(str);
    System.out.println("Generated message: " + msg);
    return msg;
  }

  public static void sleep(long average, TimeUnit unit) {
    String name = Thread.currentThread().getName();
    long timeout = Math.min(exponential(average), Math.multiplyExact(10, average));
    System.out.printf("%s sleeping %d %s...%n", name, timeout, unit);
    try {
      unit.sleep(timeout);
      System.out.println(name + " awoke.");
    } catch (InterruptedException abort) {
      Thread.currentThread().interrupt();
      System.out.println(name + " interrupted.");
    }
  }

  public static long exponential(long avg) {
    return (long) (avg * -Math.log(1 - ThreadLocalRandom.current().nextDouble()));
  }

}

1
চোখের পলকে তিনটি উত্তর! আমি কলব্যাক টাস্কটি পছন্দ করি, এ জাতীয় একটি সহজ এবং সরাসরি সমাধান। এটি পূর্বসূচীতে স্পষ্ট দেখায়। ধন্যবাদ। সিঙ্গলথ্রেডএ্যাক্সিকিউটর সম্পর্কে অন্যদের মন্তব্য সম্পর্কে: আমার হাজার হাজার সারি থাকতে পারে যার হাজার হাজার কাজ থাকতে পারে। তাদের প্রত্যেকেরই একবারে তাদের কাজগুলি প্রক্রিয়া করা প্রয়োজন তবে বিভিন্ন সারি সমান্তরালে কাজ করতে পারে। এজন্য আমি একটি একক গ্লোবাল থ্রেডপুল ব্যবহার করছি। আমি এক্সিকিউটরদের কাছে নতুন তাই দয়া করে আমাকে ভুল করে থাকলে বলুন।
শাহবাজ

5
ভাল প্যাটার্ন, আমি তবে পেয়ারাটির শ্রবণযোগ্য ভবিষ্যতের এপিআই ব্যবহার করব যা এটির খুব ভাল বাস্তবায়ন সরবরাহ করে।
পিয়েরে-হেনরি

এটি কি ফিউচার ব্যবহারের উদ্দেশ্যকে হারাবে না?
তাকেকরে

2
@ জেলফির এটি এমন একটি Callbackইন্টারফেস ছিল যা আপনি ঘোষণা করেন; একটি লাইব্রেরি থেকে না। আজকাল আমি সম্ভবত কেবলমাত্র ব্যবহার করব Runnable, Consumerবা BiConsumer, আমার কী কাজ থেকে শ্রোতার কাছে ফিরে যেতে হবে তার উপর নির্ভর করে।
এরিকসন

1
@ ভরগাভ এটি কলব্যাকের সাধারণ — একটি বাহ্যিক সত্তা নিয়ন্ত্রণকারী সত্তাকে "কল কল" বলে। আপনি কি থ্রেডটি চান যা টাস্কটি শেষ না হওয়া অবধি টাস্কটি ব্লক করে দেবে? তাহলে দ্বিতীয় থ্রেডে টাস্কটি চালানোর কী উদ্দেশ্য? যদি আপনি থ্রেডটি চালিয়ে যাওয়ার অনুমতি দেন তবে সত্য দ্বারা নির্মিত কোনও আপডেট (বুলিয়ান পতাকা, সারি-তে নতুন আইটেম ইত্যাদি) লক্ষ্য না করা পর্যন্ত এটি বারবার কিছু ভাগ করে নেওয়া (সম্ভবত একটি লুপে, তবে আপনার প্রোগ্রামের উপর নির্ভরশীল) পরীক্ষা করা দরকার এই উত্তরে বর্ণিত কলব্যাক। এটি তখন কিছু অতিরিক্ত কাজ সম্পাদন করতে পারে।
ইরিকসন

52

জাভা 8 এ আপনি কমপ্লেটেবলফিউচার ব্যবহার করতে পারেন । এখানে আমার কোডটিতে আমার উদাহরণ ছিল যেখানে আমি এটি ব্যবহার করে আমার ব্যবহারকারী পরিষেবা থেকে ব্যবহারকারীদের আনতে, এগুলি আমার দেখার বস্তুগুলিতে ম্যাপ করুন এবং তারপরে আমার ভিউ আপডেট করুন বা একটি ত্রুটি সংলাপ দেখান (এটি একটি জিইউআই অ্যাপ্লিকেশন):

    CompletableFuture.supplyAsync(
            userService::listUsers
    ).thenApply(
            this::mapUsersToUserViews
    ).thenAccept(
            this::updateView
    ).exceptionally(
            throwable -> { showErrorDialogFor(throwable); return null; }
    );

এটি তাত্পর্যপূর্ণভাবে কার্যকর করে। আমি দুটি ব্যক্তিগত পদ্ধতি ব্যবহার করছি: mapUsersToUserViewsএবং updateView


কীভাবে একজন একজন নির্বাহকের সাথে কমপ্লিটেবল ফিউচার ব্যবহার করবেন? (সংক্ষিপ্ত / সমান্তরাল দৃষ্টান্তগুলির সংখ্যার সীমাবদ্ধ করতে) এটি কি একটি ইঙ্গিতটি হবে: সিএফ: জমা দেওয়া-ফিউচারটাস্ক-এ-একজন-এক্সিকিউটর-কেন-এটি কাজ করে ?
ব্যবহারকারী 1767316

47

পেয়ারার শ্রবণযোগ্য ভবিষ্যতের এপিআই ব্যবহার করুন এবং একটি কলব্যাক যুক্ত করুন। Cf. ওয়েবসাইট থেকে:

ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
ListenableFuture<Explosion> explosion = service.submit(new Callable<Explosion>() {
  public Explosion call() {
    return pushBigRedButton();
  }
});
Futures.addCallback(explosion, new FutureCallback<Explosion>() {
  // we want this handler to run immediately after we push the big red button!
  public void onSuccess(Explosion explosion) {
    walkAwayFrom(explosion);
  }
  public void onFailure(Throwable thrown) {
    battleArchNemesis(); // escaped the explosion!
  }
});

হাই, তবে আমি যদি এই থ্রেড অনসেসের পরে থামতে চাই তবে আমি কীভাবে পারি?
DevOps85

24

আপনি FutureTaskক্লাস প্রসারিত করতে পারেন , এবং done()পদ্ধতিটিকে ওভাররাইড করতে পারেন , তারপরে FutureTaskঅবজেক্টটি যুক্ত করুন ExecutorService, সুতরাং তত্ক্ষণাত শেষ হয়ে done()গেলে পদ্ধতিটি অনুরোধ করবে FutureTask


then add the FutureTask object to the ExecutorService, আপনি দয়া করে আমাকে এটি করতে কিভাবে বলতে পারেন?
গ্যারি গৌহ

ফিউচারটাস্ক প্রসারিত করতে পারেন এমন আরও তথ্যের জন্য @ গ্যারিগৌহ এটি দেখুন , আমরা এটি মাইফিউচারটাস্ক বলতে পারি। তারপরে মাইফিউচারটাস্ক জমা দেওয়ার জন্য এক্সকিউটর সার্ভিস ব্যবহার করুন, তারপরে মাইফিউশনটাস্কের রান পদ্ধতিটি চলবে, যখন মাইফিউশনটাস্ক আপনার সম্পন্ন পদ্ধতিটি ডাকা হবে e এখানে কিছু বিভ্রান্তিকর দুটি ফিউচারটাস্ক, এবং আসলে মাইফিউচারটাস্ক একটি সাধারণ রান্নেবল।
চাওজুন ঝং

15

ThreadPoolExecutorএছাড়াও beforeExecuteএবং afterExecuteহুক পদ্ধতি রয়েছে যা আপনি ওভাররাইড করতে এবং ব্যবহার করতে পারেন। এখানে থেকে বর্ণনা ThreadPoolExecutorএর Javadocs

হুক পদ্ধতি

এই শ্রেণিটি সুরক্ষিত ওভারিডেবল beforeExecute(java.lang.Thread, java.lang.Runnable)এবং afterExecute(java.lang.Runnable, java.lang.Throwable)পদ্ধতিগুলি সরবরাহ করে যা প্রতিটি কার্য সম্পাদনের আগে এবং পরে ডাকা হয়। এগুলি কার্যকর করার পরিবেশকে কাজে লাগাতে ব্যবহার করা যেতে পারে; উদাহরণস্বরূপ, পুনর্নির্মাণ ThreadLocals, পরিসংখ্যান সংগ্রহ করা বা লগ এন্ট্রি যুক্ত করা। অতিরিক্তভাবে, পদ্ধতিটি সম্পূর্ণরূপে সমাপ্ত হয়ে terminated()গেলে কোনও বিশেষ প্রক্রিয়াকরণ সম্পাদনের জন্য ওভাররাইড করা যেতে পারে Executor। যদি হুক বা কলব্যাকের পদ্ধতিগুলি ব্যতিক্রম ছুঁড়ে ফেলে তবে অভ্যন্তরীণ কর্মী থ্রেডগুলি ফলস্বরূপ ব্যর্থ হয়ে যেতে পারে এবং হঠাৎ বন্ধ হয়ে যেতে পারে।


6

ব্যবহার ক CountDownLatch

এটি থেকে java.util.concurrentএবং এটি চালিয়ে যাওয়ার আগে বেশ কয়েকটি থ্রেড কার্যকর হওয়ার জন্য অপেক্ষা করার সঠিক উপায়।

আপনি যে কলব্যাকের প্রভাবটি দেখছেন তা অর্জনের জন্য এর জন্য কিছুটা অতিরিক্ত অতিরিক্ত কাজ দরকার। যথা, এটি নিজের দ্বারা পৃথক থ্রেডে পরিচালনা করা যা এটি ব্যবহার করে CountDownLatchএবং তার জন্য অপেক্ষা করে না, তারপরে আপনাকে যা জানাতে হবে তা যা তা অবহিত করার বিষয়ে এগিয়ে যায়। কলব্যাকের জন্য কোনও নেটিভ সমর্থন বা এফেক্টের অনুরূপ কিছু নেই।


সম্পাদনা: এখন যেহেতু আমি আপনার প্রশ্নটি আরও বুঝতে পেরেছি, আমি মনে করি আপনি অযথা খুব বেশি পৌঁছে গেছেন। আপনি যদি নিয়মিত গ্রহণ করেন তবে SingleThreadExecutorএটিকে সমস্ত কার্য দিন, এবং এটি স্থানীয়ভাবে সারিবদ্ধভাবে কাজ করবে।


সিঙ্গলথ্রেডএক্সিকিউটর ব্যবহার করে সমস্ত থ্রেড সম্পন্ন হয়েছে তা জানার সেরা উপায় কী? আমি একটি উদাহরণ দেখলাম যা কিছুক্ষণ ব্যবহার করে! এক্সিকিউটার.আইস সংজ্ঞায়িত তবে এটি খুব মার্জিত বলে মনে হয় না। আমি প্রতিটি কর্মীর জন্য একটি কলব্যাক বৈশিষ্ট্য প্রয়োগ করেছি এবং কাজ করে এমন একটি গণনা বাড়িয়েছি।
বিয়ার

5

আপনি যদি নিশ্চিত করতে চান যে কোনও কাজ একই সময়ে চলবে না তবে একটি সিঙ্গলথ্রেডএক্সেকিউটার ব্যবহার করুন । কাজগুলি জমা দেওয়ার ক্রমে প্রক্রিয়া করা হবে। আপনার এমনকি টাস্কগুলি ধরে রাখার দরকার নেই, কেবল তাদের এক্সিকিউটে জমা দিন।


2

Callbackব্যবহার পদ্ধতি প্রয়োগের জন্য সহজ কোডExecutorService

import java.util.concurrent.*;
import java.util.*;

public class CallBackDemo{
    public CallBackDemo(){
        System.out.println("creating service");
        ExecutorService service = Executors.newFixedThreadPool(5);

        try{
            for ( int i=0; i<5; i++){
                Callback callback = new Callback(i+1);
                MyCallable myCallable = new MyCallable((long)i+1,callback);
                Future<Long> future = service.submit(myCallable);
                //System.out.println("future status:"+future.get()+":"+future.isDone());
            }
        }catch(Exception err){
            err.printStackTrace();
        }
        service.shutdown();
    }
    public static void main(String args[]){
        CallBackDemo demo = new CallBackDemo();
    }
}
class MyCallable implements Callable<Long>{
    Long id = 0L;
    Callback callback;
    public MyCallable(Long val,Callback obj){
        this.id = val;
        this.callback = obj;
    }
    public Long call(){
        //Add your business logic
        System.out.println("Callable:"+id+":"+Thread.currentThread().getName());
        callback.callbackMethod();
        return id;
    }
}
class Callback {
    private int i;
    public Callback(int i){
        this.i = i;
    }
    public void callbackMethod(){
        System.out.println("Call back:"+i);
        // Add your business logic
    }
}

আউটপুট:

creating service
Callable:1:pool-1-thread-1
Call back:1
Callable:3:pool-1-thread-3
Callable:2:pool-1-thread-2
Call back:2
Callable:5:pool-1-thread-5
Call back:5
Call back:3
Callable:4:pool-1-thread-4
Call back:4

মূল নোট:

  1. আপনি যদি ফিফোর ক্রমে ক্রমানুসারে প্রক্রিয়াগুলি করতে চান তবে এর newFixedThreadPool(5) সাথে প্রতিস্থাপন করুনnewFixedThreadPool(1)
  2. আপনি যদি callbackপূর্ববর্তী কাজটির ফলাফল বিশ্লেষণ করে পরবর্তী কাজটি প্রক্রিয়া করতে চান তবে কেবল লাইনের নীচে মন্তব্য করুন un

    //System.out.println("future status:"+future.get()+":"+future.isDone());
  3. আপনি যে কোনও একটি newFixedThreadPool()দিয়ে প্রতিস্থাপন করতে পারেন

    Executors.newCachedThreadPool()
    Executors.newWorkStealingPool()
    ThreadPoolExecutor

    আপনার ব্যবহারের ক্ষেত্রে নির্ভর করে।

  4. আপনি যদি কলব্যাক পদ্ধতিটি অ্যাসিঙ্ক্রোনালি হ্যান্ডেল করতে চান

    ক। ExecutorService or ThreadPoolExecutorকলযোগ্য টাস্কে ভাগ করে নেওয়া Pass

    খ। আপনার Callableপদ্ধতিটিকে Callable/Runnableটাস্কে রূপান্তর করুন

    গ। কলব্যাক টাস্কে চাপ দিন ExecutorService or ThreadPoolExecutor


1

কেবল ম্যাট-এর উত্তর যুক্ত করতে, যা সাহায্য করেছিল, কলব্যাকের ব্যবহার দেখানোর জন্য এখানে আরও একটি মনোরম উদাহরণ রয়েছে।

private static Primes primes = new Primes();

public static void main(String[] args) throws InterruptedException {
    getPrimeAsync((p) ->
        System.out.println("onPrimeListener; p=" + p));

    System.out.println("Adios mi amigito");
}
public interface OnPrimeListener {
    void onPrime(int prime);
}
public static void getPrimeAsync(OnPrimeListener listener) {
    CompletableFuture.supplyAsync(primes::getNextPrime)
        .thenApply((prime) -> {
            System.out.println("getPrimeAsync(); prime=" + prime);
            if (listener != null) {
                listener.onPrime(prime);
            }
            return prime;
        });
}

আউটপুটটি হ'ল:

    getPrimeAsync(); prime=241
    onPrimeListener; p=241
    Adios mi amigito

1

আপনি কলযোগ্য এর বাস্তবায়ন ব্যবহার করতে পারেন

public class MyAsyncCallable<V> implements Callable<V> {

    CallbackInterface ci;

    public MyAsyncCallable(CallbackInterface ci) {
        this.ci = ci;
    }

    public V call() throws Exception {

        System.out.println("Call of MyCallable invoked");
        System.out.println("Result = " + this.ci.doSomething(10, 20));
        return (V) "Good job";
    }
}

যেখানে কলব্যাক ইন্টারফেস খুব বেসিক কিছু

public interface CallbackInterface {
    public int doSomething(int a, int b);
}

এবং এখন মূল শ্রেণি এটির মতো দেখাবে

ExecutorService ex = Executors.newFixedThreadPool(2);

MyAsyncCallable<String> mac = new MyAsyncCallable<String>((a, b) -> a + b);
ex.submit(mac);

1

এটি পেয়ারা ব্যবহার করে পাচের উত্তরের একটি বর্ধন ListenableFuture

বিশেষত, Futures.transform()রিটার্নগুলি ListenableFutureতাই অ্যাসিঙ্ক কলগুলি চেইনে ব্যবহার করা যেতে পারে। Futures.addCallback()প্রত্যাবর্তন void, তাই শৃঙ্খলার জন্য ব্যবহার করা যাবে না, তবে একটি অ্যাসিঙ্ক সমাপ্তিতে সাফল্য / ব্যর্থতা পরিচালনা করার জন্য ভাল।

// ListenableFuture1: Open Database
ListenableFuture<Database> database = service.submit(() -> openDatabase());

// ListenableFuture2: Query Database for Cursor rows
ListenableFuture<Cursor> cursor =
    Futures.transform(database, database -> database.query(table, ...));

// ListenableFuture3: Convert Cursor rows to List<Foo>
ListenableFuture<List<Foo>> fooList =
    Futures.transform(cursor, cursor -> cursorToFooList(cursor));

// Final Callback: Handle the success/errors when final future completes
Futures.addCallback(fooList, new FutureCallback<List<Foo>>() {
  public void onSuccess(List<Foo> foos) {
    doSomethingWith(foos);
  }
  public void onFailure(Throwable thrown) {
    log.error(thrown);
  }
});

দ্রষ্টব্য: অ্যাসিঙ্ক কার্যগুলি শৃঙ্খলাবদ্ধ করার পাশাপাশি, Futures.transform()প্রতিটি কাজকে একটি পৃথক নির্বাহকের উপর নির্ধারিত করার অনুমতি দেয় (এই উদাহরণে দেখানো হয়নি)।


এটা বেশ সুন্দর মনে হচ্ছে।
কায়সার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.