Mike
Mike

Reputation: 137

Multithread method

I have a few threads running that do the same task over and over. In that task it has to reauthenticate with a service to get a new sessionkey. But what happens is that all the threads try to reauthenticate.

I want to make it so that the first thread reauthenticating goes through and the others wait for that to complete and then continue as usual.

This is my original test code before implementing a solution:

public class Main {

    public static void main(String args[]){
        new Main();
    }

    public Main(){
        AuthManager authClass = new AuthManager();

        for (int i = 0; i < 5; i++) {
            Thread thr = new Thread(() -> {
                int count = 0;

                while(count < 2) { // Bad practice but just for the example.
                    if(count == 1){
                        if(authClass.reAuthenticate()) {
                            System.out.println("Reauthenticated.");

                            authClass.doStuff();
                        }
                    } else {
                        authClass.doStuff();
                    }

                    count++;
                }
            });

            thr.start();
        }

        // Keep the program running for 30 seconds.
        try {
            Thread.sleep(10 * 1000);
        } catch (InterruptedException e) {
            // ignored
        }
    }

    private class AuthManager {

        public boolean reAuthenticate(){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // ignored
            }

            System.out.println("Reauthenticating..");

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // ignored
            }

            return true; // or false when no success in the real application.
        }

        public void doStuff(){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // ignored
            }

            System.out.println("Doing stuff.");
        }

    }
}

Response:

Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Reauthenticating..
Reauthenticating..
Reauthenticating..
Reauthenticating..
Reauthenticating..
Reauthenticated.
Reauthenticated.
Reauthenticated.
Reauthenticated.
Reauthenticated.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.

The response I want:

Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Reauthenticating..
Reauthenticated.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.

How can I achieve this?

Edit I made this now with the response of @haifzhan but this won't work when I have to authenticate again later.

public class Main {

    public static void main(String args[]){
        new Main();
    }

    public Main(){
        AuthManager authClass = new AuthManager();

        for (int i = 0; i < 5; i++) {
            Thread thr = new Thread(() -> {
                int count = 0;

                while(count < 4) { // Bad practice but just for the example.
                    if(count == 1 || count == 3){
                        if(authClass.reAuthenticate()) {
                            System.out.println("Reauthenticated.");

                            authClass.doStuff();
                        }
                    } else {
                        authClass.doStuff();
                    }

                    count++;
                }
            });

            thr.start();
        }

        // Keep the program running for 30 seconds.
        try {
            Thread.sleep(10 * 1000);
        } catch (InterruptedException e) {
            // ignored
        }
    }

    private class AuthManager {

        private final AtomicBoolean isAuthorized = new AtomicBoolean();

        public synchronized boolean reAuthenticate() {
            if(!isAuthorized.get()) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // ignored
                }

                System.out.println("Reauthenticating..");

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // ignored
                }

                isAuthorized.set(true);
                return isAuthorized.get();
            }

            return isAuthorized.get();
        }

        public void doStuff(){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // ignored
            }

            System.out.println("Doing stuff.");
        }

    }
}

Response:

Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Reauthenticating..
Reauthenticated.
Reauthenticated.
Reauthenticated.
Reauthenticated.
Reauthenticated.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Reauthenticated.
Doing stuff.
Reauthenticated.
Doing stuff.
Reauthenticated.
Doing stuff.
Reauthenticated.
Doing stuff.
Reauthenticated.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.

The response I want:

Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Reauthenticating..
Reauthenticated.
Reauthenticated.
Reauthenticated.
Reauthenticated.
Reauthenticated.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Reauthenticating..
Reauthenticated.
Reauthenticated.
Reauthenticated.
Reauthenticated.
Reauthenticated.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.
Doing stuff.

Upvotes: 0

Views: 104

Answers (3)

Nicolas Filotto
Nicolas Filotto

Reputation: 45005

Here is a potential solution:

            Thread thr = new Thread(() -> {
                int count = 0;

                while(count < 2) { // Bad practice but just for the example.
                    if (count == 1 && authClass.reAuthenticate()) {
                        System.out.println("Reauthenticated.");

                        authClass.doStuff();
                    } else {
                        authClass.doStuff();
                    }

                    count++;
                }
            });

In the first code snippet, I modified a little bit the logic to call auth.doStuff() in case reAuthenticate returns false.

    private class AuthManager {

        private volatile boolean reAuthenticate;
        public boolean reAuthenticate(){
            if (!reAuthenticate) {
                synchronized (this) {
                    if (!reAuthenticate) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            // ignored
                        }

                        System.out.println("Reauthenticating..");

                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            // ignored
                        }
                        return this.reAuthenticate = true;
                    }
                }
            }
            return false;
        }

In the second code snippet, I rely on a volatile variable to be able to do a double checking of the value reAuthenticate in order to call it only once and not having to acquire any lock for later calls to reAuthenticate.

Upvotes: 2

erickson
erickson

Reputation: 269877

Use a read-write lock.

Threads can "do stuff," when they acquire the read lock, but must acquire the write lock in order to re-authenticate.

Using a read-write lock will prevent any threads from "doing stuff" during the time that another thread is re-authenticating, but won't otherwise interfere. This will minimize the number of requests that fail because of authorization, because as soon as one thread notices that re-authentication is necessary, it will request the write lock, and no other threads will be able to acquire the read lock (and fail because re-authentication is pending).

Upvotes: 1

Haifeng Zhang
Haifeng Zhang

Reputation: 31925

You can use synchronized method to change AtomicBoolean isAuthorized, this makes sure thread-safe, and other thread will not reauthenticating again.

Secondly, you have to improve your rule that how you really want to authenticate, and how to display authenticated and display doStuff. In your while loop, no matter it satisfy if condition or else condition, both print doStuff, you need to find your way to distinguish the result.

public synchronized boolean reAuthenticate(){
    if(! isAuthorized.get()){
        System.out.println("Reauthenticating..");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // ignored
        }
        isAuthorized.set(true);
        return isAuthorized.get();
    }

    return isAuthorized.get(); // or false when no success in the real application.
}

Output:

 else doStuff
    else doStuff
    else doStuff
    else doStuff
    else doStuff
    Doing stuff
    Reauthenticating..
    Doing stuff
    Doing stuff
    Doing stuff
    Doing stuff
    Reauthenticated.
    Reauthenticated.
    Reauthenticated.
    Reauthenticated.
    Reauthenticated.
    Doing stuff
    Doing stuff
    Doing stuff
    Doing stuff
    Doing stuff

Reauthenticated printed multiple times because your code print this line out once reAuthenticate() is true.

Upvotes: 1

Related Questions