3

I have created this small project to show what I want to do, but in reality it will be used in a large application which uses about 60 different threads.

I have two classes

public class Main {

    public static void main(String[] args) {
        Http http = new Http();
        Thread threadHttp = new Thread(http, "httpThread1");
        threadHttp.start();

        http.getPage("http://google.com"); // <-- This gets called on 
                                           // the main thread, 
                                           //I want it to get called from the
                                            // "httpThread1" thread
    }
}

and

public class Http implements Runnable {
    volatile OkHttpClient client;

    @Override
    public void run() {
        client = new OkHttpClient.Builder().readTimeout(10, TimeUnit.SECONDS).retryOnConnectionFailure(true).build();

    }

    public void getPage(String url) {
        Request request = new Request.Builder().url(url).build();

        try {
            Response response = client.newCall(request).execute();
            System.out.println(response.body().string());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

From the main thread, I want to be able to call the getPage method but have it execute on the httpThread1 that we started and initialized OkHttpClient client

Is this possible? how can it be done?

Arya
  • 8,473
  • 27
  • 105
  • 175
  • What's the problem with `updater.test(someVar)`? – ernest_k Jan 06 '19 at 07:15
  • `updater.test`? – joshkmartinez Jan 06 '19 at 07:15
  • wouldn't test run on the main thread though? not the new thread I started for updater? – Arya Jan 06 '19 at 07:16
  • 1
    @Arya Your last sentence says `How can I call test from the main thread ...` – ernest_k Jan 06 '19 at 07:16
  • @ernest_k yes, but I want the method to run on the new method that we started for updater – Arya Jan 06 '19 at 07:17
  • 1
    @Arya It's a bit unclear. Why not pass `var` the same way you pass `new Date()` to the constructor and call `test` from within the `run()` method? If the `var` value is only known to the main thread after `threadUpdater` has started, then you might want to use things like callbacks... – ernest_k Jan 06 '19 at 07:22
  • I will edit this with more clarity. I'm working on it – Arya Jan 06 '19 at 07:24
  • "the method to run on the new method" - Its a bit unclear like @ernest_k said. Why do you need to make another thread anyways? – joshkmartinez Jan 06 '19 at 07:24
  • I have updated the question. Should be clear now – Arya Jan 06 '19 at 07:36
  • @Arya I think you're using the `run` method for the wrong thing. You should be doing in `run` what you're currently doing in `getPage` – ernest_k Jan 06 '19 at 07:39
  • I understand I can do that in this example. But I need this for a much bigger project and after initialization, there are times that a different thread needs to call and pass a parameter to another thread – Arya Jan 06 '19 at 07:41

3 Answers3

1

Runnable#run is the method designed to do the actual work of a Runnable object. So you would have to make it do what you're currently doing in getPage.

You can use state to store url, and save the response in a different field. See further comments about how you can refactor this to simplify it even further. But from the current code, the simplest changes could be:

class Http implements Runnable {

    //initialize Http. This can be done better perhaps
    volatile OkHttpClient client = new OkHttpClient.Builder()
            .readTimeout(10, TimeUnit.SECONDS)
            .retryOnConnectionFailure(true).build();

    private Response response;

    private String url;

    public Http(String url) {
        this.url = url;
    }

    @Override
    public void run() {
        this.getPage(this.url);
    }

    public void getPage(String url) {
        Request request = new Request.Builder().url(url).build();

        try {
            this.response = client.newCall(request).execute();
            System.out.println(response.body().string());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

And in your main method:

Http http = new Http("http://google.com");
Thread threadHttp = new Thread(http, "httpThread1");
threadHttp.start();
threadHttp.join();
Response resp = http.getResponse();

However, this can be substantially simplified with the use of futures. For example, it could look as simple as:

class Http {
    volatile OkHttpClient client = new OkHttpClient.Builder()
            .readTimeout(10, TimeUnit.SECONDS)
            .retryOnConnectionFailure(true).build();

    public Response getPage(String url) {
        Request request = new Request.Builder().url(url).build();

        try {
            this.response = client.newCall(request).execute();
            System.out.println(response.body().string());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

And, using futures, your main method can look even simpler:

public static void main(String[] args) throws Exception {
    Http http = new Http();
    CompletableFuture<Response> future = 
            CompletableFuture.supplyAsync(() -> http.getPage("http://google.com"));

    //the preceding statement will call `getPage` on a different thread.
    //So you can do other things before blocking with next statement

    Response resp = future.join();
}

You can even use a thread pool with supplyAsync if you need more control over how asynchronous tasks run.

ernest_k
  • 44,416
  • 5
  • 53
  • 99
  • The futures example is very interesting. I'm going to see if I can use that in the main project – Arya Jan 06 '19 at 08:37
  • In the Main class I'm getting a red line under `.supplyAsync(()` with error "The method supplyAsync(Supplier) in the type CompletableFuture is not applicable for the arguments (() -> {})" – Arya Jan 06 '19 at 08:48
  • Is `getPage` a `void` method in your version of `Http`? – ernest_k Jan 06 '19 at 08:49
  • @Arya If you prefer, you can keep it void (maybe you want to retrieve the response with a getter). If it's void, you'd have to use `CompletableFuture future = CompletableFuture.runAsync(() -> http.get...` – ernest_k Jan 06 '19 at 08:52
  • @ernest_k Will this example work if the user wants to supply a new URL for each thread? In your code, once you set the URL, each thread will call on that URL. What if the user wants to supply a custom URL to each thread using the same Http object? – Prashant Pandey Jan 06 '19 at 08:58
  • @PrashantPandey That's why I suggested the second approach. It doesn't hold state for any particular request. So the `Http` object can be resused as many times as desired (assuming `OkHttpClient` is thread-safe). It's just important to note that `runAsync` and `supplyAsync` use the fork-join pool. If more threads are needed, then one should use a custom thread pool – ernest_k Jan 06 '19 at 09:02
0

You can call the test method within the Updater class like this: updater.test(yourVarHere)
To call a method within a separate thread see this question
You might also want to check out the Java concurrency tutorial

joshkmartinez
  • 654
  • 1
  • 12
  • 35
0

Based on your question, I think that you might do this:

class HttpThread extends Thread {
     volatile OkHttpClient client;

     HttpThread(Runnable target, String name) {
        super(target, name);
     }

     @Override
     public void run() {
        client = new OkHttpClient.Builder().readTimeout(10, TimeUnit.SECONDS).retryOnConnectionFailure(true).build();
     }

     public void getPage(String url) {
        Request request = new Request.Builder().url(url).build();
        try {
            Response response = client.newCall(request).execute();
            System.out.println(response.body().string());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 }

And in Main class:

public class Main {

   public static void main(String[] args) {
       Thread httpThread = new HttpThread(http, "httpThread1");
       httpThread.start();
       httpThread.getPage("http://google.com"); 
    }
}
Jonathan JOhx
  • 5,784
  • 2
  • 17
  • 33
  • I like this but unfortunately I can't use it since in my main project I have to use the `Runnable` method as the class is already extending another class – Arya Jan 06 '19 at 08:18
  • https://stackoverflow.com/questions/21871073/java-thread-vs-runnable It's the almost same https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html – Jonathan JOhx Jan 06 '19 at 08:25
  • In the main project I have `public class OkHttp extends WebSocketListener implements Runnable` I have to do `extends WebSocketListener` not sure how I can use your example – Arya Jan 06 '19 at 08:30