Search code examples
javaservletsrunnablecallable

Convert Runnable.run() to Callable.call() in JAVA Servlet


I have problem converting my code with the runnable interface to the callable interface in the following code. I need to change, because I need to return a Sting[][] isRs by the threads.

When I just change the interface to callable and chande .run() to .call(), then new Thread(new Worker(startSignal, doneSignal, i)).start(); wont work.

CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(3); // 3 tasks

class Worker implements Runnable {
    private final CountDownLatch startSignal;
    private final CountDownLatch doneSignal;
    private final int threadNumber;

    // you can pass additional arguments as well
    Worker(CountDownLatch startSignal, CountDownLatch doneSignal, int threadNumber) {
        this.startSignal = startSignal;
        this.doneSignal = doneSignal;
        this.threadNumber = threadNumber;
    }

    public void run() {
        try {
            startSignal.await();

            if (threadNumber == 1) {
                String[][] isRs = getIS(erg1, erg2, request);
            }

            if (threadNumber == 2) {
                getIW(erg1, erg2, request);
            }

            if (threadNumber == 3) {
                getIN(search_plz, request);
            }

            doneSignal.countDown();
        } catch (InterruptedException ex) {
            System.out.println(ex);
        }
    }
}

// 3 new threads are started
for (int i = 1; i <= 3; i++) {
    new Thread(new Worker(startSignal, doneSignal, i)).start();
}

startSignal.countDown(); // let all threads proceed
try {
    doneSignal.await(); // wait for all to finish
    // all 3 tasks are finished and do whatever you want to do next
} catch (Exception e) {

}

Solution

  • You cannot pass a Callable into a Thread to execute.

    Use the ExecutorService to execute the Callable object.

    You can give it Callable objects to run using its submit() method:

    <T> Future<T> submit(Callable<T> task)

    Your class should look like:

    class Worker {
    
        private final CountDownLatch startSignal;
        private final CountDownLatch doneSignal;
        private final int threadNumber;
    
        Worker(
            CountDownLatch startSignal,
            CountDownLatch doneSignal,
            int threadNumber
        ){
    
            this.startSignal = startSignal;
            this.doneSignal = doneSignal;
            this.threadNumber = threadNumber;
    
        }
    
        public String[][] getSomeStrArrArr() {
    
            try {
    
                startSignal.await();
    
                if (threadNumber == 1) {
                    System.out.println("Running thread number 1");
                }
    
                if (threadNumber == 2) {
                    System.out.println("Running thread number 2");
                }
    
                if (threadNumber == 3) {
                    System.out.println("Running thread number 3");
                }
    
                doneSignal.countDown();
    
            } catch (InterruptedException ex) {
    
                System.out.println(
                        "Thread number "+threadNumber+" has been interrupted."
                );
    
            }
    
            // replace these 2 lines with the actual code to get the String[][]
            String[][] someStrArrArr = new String[1][1];
            someStrArrArr[0][0] = "Done with thread number "+threadNumber;
    
            return someStrArrArr;
    
        }
    
        public Callable<String[][]> getSomeCallableStrArrArr(){
            return new Callable<String[][]>() {
                public String[][] call() throws Exception {
                    return getSomeStrArrArr();
                }
            };
        }
    
    }
    

    And you'd start it like:

        ExecutorService pool = Executors.newFixedThreadPool(3);
        Set<Future<String[][]>> set = new HashSet<Future<String[][]>>();
        CountDownLatch startSignal = new CountDownLatch(1);
        CountDownLatch doneSignal = new CountDownLatch(3);
        for (int i=1;i<=3;i++) {
            Worker worker = new Worker(startSignal,doneSignal,i);
            Callable<String[][]> callable =
                    worker.getSomeCallableStrArrArr();
            Future<String[][]> future = pool.submit(callable);
            set.add(future);
        }
    

    And, to get and print the result strings:

        for(Future<String[][]> future : set){
            String[][] result = future.get();
            for (String[] strArr: result){
                for (String str: strArr){
                    System.out.println(str);
                }
            }
        }
    

    But this design can be improved. Have a look at the following documentation on Callable to see how it differenciates from Runnable and how you can get advantage from those differences and implent it properly:

    Interface Callable

    https://blogs.oracle.com/CoreJavaTechTips/entry/get_netbeans_6?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+corejavatechtips+(Core+Java+Technologies+Tech+Tips)

    Also check out this link where I've written an example based on your code you can run and fiddle with: http://ideone.com/blUQm0