user2896120
user2896120

Reputation: 3282

Creating 2 threads and each running different tasks

I want to create two threads in my application that'll run two methods. I'm using the builder design pattern where inside the build method I have something like this, request is the Object that is passed:

Rules rule;
        Request build() {
            Request request = new Request(this);
        //I want one threat to call this method
        Boolean isExceeding = this.rule.volumeExceeding(request);

        //Another thread to call this method
        Boolean isRepeating = this.rule.volumeRepeating(request);
        
        //Some sort of timer that will wait until both values are received,
        //If one value takes too long to be received kill the thread and continue with
        //whatever value was received.

        ..Logic based on 2 booleans..
        return request;
    }
        
    

Here's how this class looks like:

public class Rules {

    public Boolean volumeExceeding(Request request) {

        ...some...logic...
        return true/false;
    }


    public Boolean volumeRepeating(Request request) {
       
        ...some...logic...
        return true/false;
    }
}

I have commented in the code what I'd like to happen. Basically, I'd like to create two threads that'll run their respective method. It'll wait until both are finished, however, if one takes too long (example: more than 10ms) then return the value that was completed. How do I create this? I'm trying to understand the multithreading tutorials, but the examples are so generic that it's hard to take what they did and apply it to something more complicated.

Upvotes: 0

Views: 609

Answers (2)

akuzminykh
akuzminykh

Reputation: 4723

One way to do that is to use CompletableFutures:

import java.util.concurrent.CompletableFuture;

class Main {
    
    private static final long timeout = 1_000; // 1 second

    
    static Boolean volumeExceeding(Object request) {
        System.out.println(Thread.currentThread().getName());
        final long startpoint = System.currentTimeMillis();
        
        // do stuff with request but we do dummy stuff
        for (int i = 0; i < 1_000_000; i++) {
            if (System.currentTimeMillis() - startpoint > timeout) {
                return false;
            }
            Math.log(Math.sqrt(i));
        }
        return true;
    }
    
    
    static Boolean volumeRepeating(Object request) {
        System.out.println(Thread.currentThread().getName());
        final long startpoint = System.currentTimeMillis();
        
        // do stuff with request but we do dummy stuff
        for (int i = 0; i < 1_000_000_000; i++) {
            if (System.currentTimeMillis() - startpoint > timeout) {
                return false;
            }
            Math.log(Math.sqrt(i));
        }
        return true;
    }
    

    public static void main(String[] args) {
        final Object request = new Object();
        
        CompletableFuture<Boolean> isExceedingFuture = CompletableFuture.supplyAsync(
                () -> Main.volumeExceeding(request));
        CompletableFuture<Boolean> isRepeatingFuture = CompletableFuture.supplyAsync(
                () -> Main.volumeRepeating(request));
        
        Boolean isExceeding = isExceedingFuture.join();
        Boolean isRepeating = isRepeatingFuture.join();
        
        System.out.println(isExceeding);
        System.out.println(isRepeating);
    }
}

Notice that one task takes significantly longer than the other.

What's happening? You supply those tasks to the common pool by using CompletableFuture for execution. Both tasks are executed by two different threads. What you've asked for is that a task is stopped when it takes too long. Therefore you can simply remember the time when a task has started and periodically check it against a timeout. Important: Do this check when the task would return while leaving the data in a consistent state. Also note that you can place multiple checks of course.

Here's a nice guide about CompletableFuture: Guide To CompletableFuture

Upvotes: 2

TreffnonX
TreffnonX

Reputation: 2930

If I understand your question correctly, then you should do this with a ticketing system (also known as provider-consumer pattern or producer-consumer pattern), so your threads are reused (which is a significant performance boost, if those operations are time critical).

The general idea should be:

application initialization

  • Initialize 2 or more "consumer" threads, which can work tickets (also called jobs).

runtime

  • Feed the consumer threads tickets (or jobs) that will be waited on for (about) as long as you like. However depending on the JVM, the waiting period will most likely not be exactly n milliseconds, as most often schedulers are more 'lax' in regards to waiting periods for timeouts. e.g. Thread.sleep() will almost always be off by a bunch of milliseconds (always late, never early - to my knowledge).
  • If the thread does not return after a given waiting period, then that result must be neglected (according to your logic), and the ticket (and thus the thread) must be informed to abort that ticket. It is important that you not interrupt the thread, since that can lead to exceptions, or prevent locks from being unlocked.

Remember, that halting or stopping threads from the outside is almost always problematic with locks, so I would suggest, your jobs visit a possible exit point periodically, so if you stop caring about a result, they can be safely terminated.

Upvotes: 0

Related Questions