简体   繁体   中英

What if I need to return an object from thread's runnable object?

What I need is to return token object as a result of adapter.authenticate() operation placed in thread. Have no idea how to return an object from there, please advice. Thank you.

public Token authenticate(final String email, final String password) {

        Thread mThread = new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    token = adapter.authenticate(email, password); // I need token to return with authenticate() method.

                } catch (NotAuthenticatedException e) {

                }
            }
        });
        mThread.start();
    }

Implement the Callable interface. Treat the call method just like run, and you can return your token.

Try this:

public Token authenticate(final String email, final String password) {
    final Token[] tokens = new Token[1];
    Thread mThread = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                tokens[0] = adapter.authenticate(email, password);
            } catch (NotAuthenticatedException e) {

            }
        }
    });
    mThread.start();
    mThread.join();
    return tokens[0];
}

Explanation:

  • You can't make token a Token because it has to be final .
  • You need to join the thread, or else the authenticate is liable to return before the thread has set the token in tokens[0] .

It should also be noted that you are not achieving much by using a thread in that way. What you are doing is really just an expensive way of calling adapter.authenticate(email, password) .


If your aim is to avoid blocking the UI while waiting for authentication, the solution needs to be more complicated. Basically, you need to change your application so that the view elements that require authentication to have happened start out as disabled (eg they are greyed out / ignore clicks / whatever). You do the authentication is a separate thread as above, and when it completes you get it to send an event to the UI thread. On receiving that event, the UI thread should then enable the disabled view elements.

Why would you want to start something in another thread and then wait for the outcome? Just call adapter.authenticate() directly.

If you need to the results of an asynchronous operation in the gui, then you need to use a callback method. you need to essentially use 2 runnables. the first runnable does the work on a separate thread. when that work is complete, the result should be passed into a second runnable which is passed to SwingUtilities.invokeLater . The second runnable will be run on the EDT thread at the next available opportunity and can safely update the ui with the results of the asynchronous operation.

If you don't want the UI to freeze, you need to do something like this:
(Source is for illustration purpose only, has compile error etc. (for brevity))

class Sample {
    static final Executor exec = Executors.newCachedThreadPool();
    final Adapter adapter = new Adapter();

    public Token processRequest(final Email email, final Password password){
        Future<Token> future = exec.submit(
                   new Callable<Token>(){
                       @Override
                       public Token call(){
                           return adapter.authenticate(email, password);
                       }
                   });
       //do some stuff
       if(future.isDone()){
           //Ah, it's done.
           return future.get();
       }
       //Not done yet. do some more useful stuff.

       //Nothing to do anymore. Just block on the future and 
       // wait until it's finished.
       return future.get();
    }
}

Or of course you can store the future somewhere associated with the session and only return when requested by the user etc.

Your authenticate() method cannot return Token (unless it can be a placeholder object) since for it to return straight away, the result may not have been discovered yet.

Have the outer object implement a callback method, something like void authenticationComplete(Token authToken) and have your Runnable call that method with the result when it arrives.

class Authenticator {
  ...
  public void authenticate(final String email, final String password) {
     new Thread(new Runnable() {
       public void run() { 
         Authenticator.this.authenticationComplete(
           adapter.authenticate(email, password)
         );
       }
     }).start();
  }

  public void authenticationComplete(Token authToken) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        // Update your UI
      }
    });
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM