0

I tried out multithreading for a project I'm making. in the project I need to do a certain calculation multiple times every time I call for a certain function. I tried making some testing code to understand how to do it, but I can't get it to work properly (the code seems to work perfectly when I debug it, but if I run it normally it doesn't work past the first cycle).

in the code there is an endless loop that mimics my project's calling for a function multiple times. I tried to do it so the thread runs while changeflag is true, and change the flag to false after every run of the calculation so it would stop from calculating it again and again, and after "calling" the function I change it to true back, so it would be able to calculate again.

following is my code:

import java.util.ArrayList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<Result> queue = new SynchronousQueue<>();
        int loops = 0;
        MyThread[] arr = new MyThread[10];
        ArrayList<Result> ress = new ArrayList<>();

        for (int i = 0; i < arr.length; i++) {
            arr[i] = new MyThread(i, queue);
            arr[i].start();
        }
        while (true) {
            System.out.println(loops++);
            while (ress.size() < arr.length){
                ress.add(queue.take());
            }
            while (!ress.isEmpty()){
                arr[ress.get(0).getSign()].setChangeflag(true);
                ress.remove(0);
            }
        }
    }
}

import java.util.Random;
import java.util.concurrent.BlockingQueue;

public class MyThread extends Thread{
    private boolean changeflag = true;
    private boolean runflag = true;
    private int sign;
    private BlockingQueue<Result> queue;
    Random rnd = new Random();

    public MyThread(int sign, BlockingQueue<Result> queue){
        this.sign = sign;
        this.queue = queue;
    }

    public void run(){
        while (runflag){
            if(changeflag){
                changeflag = false;
                try {
                    queue.put(sense());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public Result sense(){
        return new Result( rnd.nextInt(10), sign);
    }

    public synchronized void setChangeflag(boolean changeflag) {
        this.changeflag = changeflag;
    }
}
public class Result {
    private double res;
    private int sign;

    public Result(double res, int sign) {
        this.res = res;
        this.sign = sign;
    }

    public int getSign() {
        return sign;
    }
}
plank
  • 1
  • 2
  • 1
    "the code seems to work perfectly when I debug it, but if I run it normally it doesn't work past the first cycle" Welcome to concurrency! I don't quite understand what you're trying to achieve with this code. Can you explain what is your goal instead? Why don't you just run a thread to calculate your result (or use a thread pool)? – m0skit0 Mar 27 '20 at 22:14
  • Im trying to make it so everytime a certain function is called, I'll get a new set of results. in my actual code, sense() doesn't return a random number, but does a certain calculation, and I don't want it to that calculation unless stuff have changed. @m0skit0 – plank Mar 27 '20 at 22:55
  • @m0skit0 I assume you didn't get a notification for my reply. so I'm trying to ping you again. thanks a lot for your help! – plank Mar 28 '20 at 16:13
  • I got it the first time but forgot to reply, sorry. If that's what you want, what's the use for the while in your run() method? Why do you want to loop there? Why is there a blocking queue too? – m0skit0 Mar 28 '20 at 19:49
  • that's how my friend suggested to do it. I basically keep the the threads running but change the changeflag when I call the said function so it will run the calculation again. and the blocking queue is so the main thread will wait until all of the calculations are done till it continues. if you have a better suggestion to do it, im all ears! like I said, I'm new to threads and that's how my friend suggested me to do it. @m0skit0 – plank Mar 28 '20 at 21:01
  • Blocking the main thread kind of defeats the purpose of using other threads in the first place, don't you think? – m0skit0 Mar 29 '20 at 00:03

2 Answers2

0

I recommend using Executors.newCachedThreadPool(). This will return an ExecutorService which you can use to queue your calculations using submit(Callable), which returns a Future on which you can block as desired. If you queue many tasks you can just keep a list of Futures as needed or a list of tasks then submit them to the ExecutorService.

Also note it's usually not recommended to extend from Thread.

Hope this helps!

m0skit0
  • 25,268
  • 11
  • 79
  • 127
0

The only reason I, at least, can see why you need Threads here is to do other work while waiting for the sense method to complete in the background. For example render some graphics or interact with the user.

If your main Thread is required to wait until all the sense job is complete for each request, then you don't need Threads. Just call the method sense directly in the main Thread.

On the other hand, if you need a background Thread doing the sense job while the main Thread is doing other work, then you will need two Threads: one is the main, and the other is the background-job. Then you probably need to have a producer-consumer pattern, where the producer (the main Thread) creates the requests and the consumer (the background Thread) executes the sense method. But then it seems like the roles are turned around again like you want to wait in the main Thread all the requests to complete after you submit them. If that is the case then you can start all the MyThreads and then call join on them when you are ready to wait for their results. For example:

import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;

public class Main {

    public static class Result {
        private final int index;
        private final Object value;

        public Result(final int index,
                      final Object value) {
            this.index = index;
            this.value = value;
        }

        public int getIndex() {
            return index;
        }

        public Object getValue() {
            return value;
        }
    }

    public static class MyRunnable implements Runnable {
        private final int index;
        private final Collection<Result> sharedResults;

        public MyRunnable(final int index,
                          final Collection<Result> sharedResults) {
            this.index = index;
            this.sharedResults = Objects.requireNonNull(sharedResults);
        }

        @Override
        public void run() {
            final Result res = sense(); //Calculating outside the synchronized block.
            synchronized (sharedResults) { //Synchronizing, because the actual instance of this collection might not be synchronized.
                sharedResults.add(res);
            }
        }

        private Result sense() {
            return new Result(index, "Value" + index);
        }
    }

    public static void main(final String[] args) {
        final Thread[] t = new Thread[10];
        final Collection<Result> sharedResults = new ArrayList<>();

        for (int i = 0; i < t.length; ++i) {
            t[i] = new Thread(new MyRunnable(i, sharedResults));
            t[i].start();
        }

        for (final Thread thread: t)
            try { thread.join(); } catch (final InterruptedException ix) { ix.printStackTrace(); }

        sharedResults.forEach(res -> System.out.println("Result " + res.getIndex() + " with value \"" + res.getValue() + "\"."));
    }
}

Another way is to use an ExecutorService like suggested by @m0skit0 and utilize the returned Future objects to wait for the results.

gthanop
  • 3,035
  • 2
  • 10
  • 27