StefanoN
StefanoN

Reputation: 157

Java multi threading - run threads run method only once in sequence

In my applications there are an n number of actions that must happen, one after the other in sequence, for the whole life of the program. Instead of creating methods which implement those actions and calling them in order in a while(true) loop, I decided to create one thread for each action, and make them execute their run method once, then wait until all the other threads have done the same, wait for its turn, and re-execute again, and so on...

To implement this mechanism I created a class called StatusHolder, which has a single field called threadTurn (which signifies which thread should execute), a method to read this value, and one for updating it. (Note, this class uses the Singleton design pattern)

package Test;

public class StatusHolder
{
    private static volatile StatusHolder statusHolderInstance = null;
    public static volatile int threadTurn = 0;

    public synchronized static int getTurn()
    {
        return threadTurn;
    }

    public synchronized static void nextTurn()
    {
        System.out.print("Thread turn: " + threadTurn + " --> ");

        if (threadTurn == 1)
        {
            threadTurn = 0;
        }
        else
        {
            threadTurn++;
        }

        System.out.println(threadTurn);

        //Wake up all Threads waiting on this obj for the right turn to come
        synchronized (getStatusHolder())
        {
            getStatusHolder().notifyAll();
        }
    }

    public static synchronized StatusHolder getStatusHolder()
    {//Returns reference to this object

        if (statusHolderInstance == null)
        {
            statusHolderInstance = new StatusHolder();
        }

        return statusHolderInstance;
    }
}

Then I have, let's say, two threads which must be execute in the way explained above, t1 and t2.

T1 class looks like this:

package Test;

public class ThreadOne implements Runnable
{
    @Override
    public void run()
    {
        while (true)
        {
            ThreadUtils.waitForTurn(0);

            //Execute job, code's not here for simplicity
            System.out.println("T1 executed");

            StatusHolder.nextTurn();
        }
    }
}

And T2 its the same, just change 0 to 1 in waitForTurn(0) and T1 to T2 in the print statement.

And my main is the following:

package Test;

public class Main
{
    public static void main(String[] args) throws InterruptedException
    {
        Thread t1 = new Thread(new ThreadOne());
        Thread t2 = new Thread(new ThreadTwo());

        t1.start();
        t2.start();

    }
}

So the run method goes like this: At the start of the loop the thread looks if it can act by checking the turn value with the waitForTurn() call:

package Test;

public class ThreadUtils
{
    public static void waitForTurn(int codeNumber)
    { //Wait until turn value is equal to the given number

        synchronized (StatusHolder.getStatusHolder())
        {
            while (StatusHolder.getTurn() != codeNumber)
            {
                try
                {
                    StatusHolder.getStatusHolder().wait();
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        }
    }
}

If the two values are equal, the thread executes, otherwise it waits on the StatusHolder object to be awaken from the nextTurn() call, because when the turn value changes all the threads are awaken so that they can check if the new turn value is the one they are waiting for so they can run.

Note thatnextTurn() cycles between 0 and 1: that is because in this scenario I just have two threads, the first executes when the turn flag is 0, and the second when its 1, and then 0 again and so on. I can easily change the number of turns by changing this value.

The problem: If I run it, all goes well and seems to work, but suddenly the output console stops flowing, even if the program doesn't crash at all. I tried to put a t1.join() and then a print in the main but that print never executes, this means that the threads never stop/dies, but instead they remain locked sometimes. This looks to be even more evident if I put three threads: it stops even sooner than with two threads.

I'm relatively new to threads, so I might be missing something really stupid here...

EDIT: I'd prefer not to delete a thread and create a new one every time: creating and deleting thousands of objs every second seems a big work load for the garbage collector.

The reason why I'm using threads and not functions is because in my real application (this code is just simplified) at a certain turn there actually are multiple threads that must run (in parallel), for example: turn 1 one thread, turn 2 one thread, turn 3 30 threads, repeat. So I thought why not creating threads also for the single functions and make the whole think sequential.

Upvotes: 1

Views: 6172

Answers (2)

erickson
erickson

Reputation: 269797

This is a bad approach. Multiple threads allow you to execute tasks concurrently. Executing actions "one after the other in sequence" is a job for a single thread.

Just do something like this:

List<Runnable> tasks = new ArrayList<>();
tasks.add(new ThreadOne()); /* Pick better names for tasks */
tasks.add(new ThreadTwo());
...
ExecutorService worker = Executors.newSingleThreadExecutor();
worker.submit(() -> {
    while (!Thread.interrupted()) 
        tasks.forEach(Runnable::run);
});
worker.shutdown();

Call worker.shutdownNow() when your application is cleanly exiting to stop these tasks at the end of their cycle.

Upvotes: 4

Taha Kerrouzi
Taha Kerrouzi

Reputation: 177

you can use Semaphore class it's more simple

class t1 :

public class t1 implements Runnable{
   private Semaphore s2;
     private Semaphore s1;
    public t1(Semaphore s1,Semaphore s2){
    this.s1=s1;
    this.s2=s2;
    }
      public void run()
    {
        while (true)
        {
            try {
                s1.acquire();
            } catch (InterruptedException ex) {
                Logger.getLogger(t1.class.getName()).log(Level.SEVERE, null, ex);
            }

            //Execute job, code's not here for simplicity
            System.out.println("T1 executed");
       s2.release();

        }
    }
}

class t2:

public class t2 implements Runnable{
    private Semaphore s2;
     private Semaphore s1;
    public t2(Semaphore s1,Semaphore s2){
    this.s1=s1;
    this.s2=s2;
    }

      public void run()
    {
        while (true)
        {
            try {
                s2.acquire();
            } catch (InterruptedException ex) {
                Logger.getLogger(t2.class.getName()).log(Level.SEVERE, null, ex);
            }

            //Execute job, code's not here for simplicity
            System.out.println("T2 executed");

            s1.release();
        }
    }
}

class main:

public class Testing {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
         Semaphore s2=new Semaphore(0);
         Semaphore s1=new Semaphore(1);
       Thread th1 = new Thread(new t1(s1,s2));
        Thread th2 = new Thread(new t2(s1,s2));

        th1.start();
        th2.start();
}}

Upvotes: 1

Related Questions