First, your question suffers from some problems:
- Your code has several compilation errors whose correction is not trivial. First, the variables
primeiro
and ultimo
were not declared, and due to the modifier final
in the parameters, also can not be just a simple case of having forgotten to rename the first
and the last
, even more given the fact that you clearly use ultimo
and last
as two distinct variables.
- You also did not provide the method code
calcularQuantidadeDivisivelSerial
, making it difficult for us to see what is happening, even more so if there can be some problem within this method.
- The variable
arrayFutureTasks
has also not been declared and clearly is not the same thing as taskList
. And that’s another reason why the code doesn’t compile.
- You have not reported what values you used in the function to get the result you got, and without that it is difficult to answer your question "Looking at the Futuretask arrayList I really saw that one is with the value it should after the calculation and the other is with zero, but why is this happening?".
These above problems are reason enough to close your question as "it’s not clear what you’re asking", but I’ll try to answer anyway.
First let’s fix the compilation problems:
I’ll assume the class name is FutureTaskDemo
and put the imports
necessary.
The method calcularQuantidadeDivisivelSerial
:
public static int calcularQuantidadeDivisivelSerial(int first, int last, int divisor) {
int result = 0;
for (int i = first; i <= last; i++) {
if (i % divisor == 0) result++;
}
return result;
}
- I will assume that
primeiro
and ultimo
should be declared the first time a value is assigned to them. So this:
primeiro =0; ultimo = primeiro+quantidadeFatiasPorThread;
Becomes that:
int primeiro = 0;
int ultimo = primeiro + quantidadeFatiasPorThread;
- Since
primeiro
and ultimo
are not variable final
(and not effectively-final
, once they mutate), the compiler will complain that these variables are being used within the code of the anonymous class. So I’m gonna turn it:
arrayFutureTasks[i] = new FutureTask<Integer>(new Callable<Integer>() {
@Override
public Integer call() {
return FutureTaskDemo.calcularQuantidadeDivisivelSerial(primeiro, ultimo, divisor);
}
});
Therein:
int primeiroFinal = primeiro;
int ultimoFinal = ultimo;
arrayFutureTasks[i] = new FutureTask<>(() -> calcularQuantidadeDivisivelSerial(primeiroFinal, ultimoFinal, divisor));
And I’ve already used lambda-Xpression instead of anonymous class up there.
- The variable
arrayFutureTasks
has not been declared. So I will change that:
arrayFutureTasks = new FutureTask[threadNum];
That’s why:
@SuppressWarnings("unchecked")
FutureTask<Integer>[] arrayFutureTasks = (FutureTask<Integer>[]) new FutureTask<?>[threadNum];
Now your code must be compilable, but showing incorrect results. There is nothing wrong with it regarding the threads or the FutureTask
. Your problem is that your code has a number of mathematical errors.
Let’s try to pack it up:
- The parameter
first
is not used anywhere. I assume you wanted it to be used to calculate the values of the first slice to be computed, otherwise the code will produce an incorrect result if the first number of the track is not zero. So that:
int primeiro = 0;
int ultimo = primeiro + quantidadeFatiasPorThread;
Becomes that:
int primeiro = first;
int ultimo = primeiro + quantidadeFatiasPorThread;
- The size of the first slice is wrong. The reason for this is the way the variable
ultimo
has its first value computed. For example, if primeiro
is equal to 5 and quantidadeFatiasPorThread
is equal to 4, then we will have to:
last = first + quantityFatialsPorThread = 5 + 4 = 9
And so the first slice has the elements [5, 6, 7, 8, 9], ie 5 elements. However the fact that the slice has 5 elements is contrary to the purpose of the variable quantidadeFatiasPorThread
that has as value 4. Here is the solution:
int primeiro = first;
int ultimo = primeiro + quantidadeFatiasPorThread - 1;
- Your slice size calculation has problems. The first is that as you do not use the
first
in it, and therefore if the first
is greater than zero, lower than first
will be incorrectly considered in the calculation. If the first
is less than zero, negative values will be incorrectly disregarded. Therefore, to calculate the size of the slice I have to take into account only the elements between the first
and the last
. And so this:
int quantidadeFatiasPorThread = (last / threadNum);
Becomes that:
int quantidadeFatiasPorThread = (last - first + 1) / threadNum;
Maybe you’re wondering why the + 1
. His reason is that I want to also count the element of first
. For example, if I want all elements 1 through 6, then I will want these elements: [1, 2, 3, 4, 5, 6], but if I do 6 - 1
the result is 5. Similarly if I want the elements [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], I have 11 elements, not 10 (can count to check if you doubt). This is basically because I am including, not excluding, the elements first
. The purpose of + 1
also becomes obvious when the last
and the first
have the same value.
- Still in calculating the slice size we still have one more problem: if the division is not accurate, the rounding will be down. This causes at the end of the interval between
first
and last
, some numbers may be left over without getting into any slice. To solve this you could use float
or double
, but it’s much easier to just round up if necessary:
int quantidadeFatiasPorThread = (last - first + 1) / threadNum;
if ((last - first + 1) % threadNum != 0) quantidadeFatiasPorThread++;
- Another interesting thing is this passage:
if(ultimo > last){
int quantidadeMais = ultimo - last;
ultimo = ultimo - quantidadeMais;
}
Let’s do a little math:
quantityMore = last - last
ultimo2 = last - quantityMore
Substituting quantityMore in the second equation we have:
last 2 = last - (last - last)
last 2 = last - last
ultimo2 = last
And this should be something obvious, because the ultimo
cannot be greater than the last
, the highest value he can assume is the last
. You don’t need a more complicated formula for this. Soon:
if (ultimo > last) ultimo = last;
Now your code should be working properly, but let’s improve it a little more.
- First these debug instructions:
System.out.println(i+" "+threadNum);
System.out.println("primeiro :"+primeiro+" ultimo: "+ultimo);
They look much better if you visualize it like this:
System.out.println("i: " + i + " threadNum: " + threadNum + " primeiro: " + primeiro + " ultimo: " + ultimo);
- Let’s put an extra parameter in the method
calcularQuantidadeDivisivel
to specify the number of threads instead of leaving it fixed as 2. So this:
public static int calcularQuantidadeDivisivel(final int first, final int last, final int divisor)
throws InterruptedException, ExecutionException {
int amount = 0;
int threadNum = 2;
Becomes that:
public static int calcularQuantidadeDivisivel(int first, int last, int divisor)
throws InterruptedException, ExecutionException
{
return calcularQuantidadeDivisivel(first, last, divisor, 2);
}
public static int calcularQuantidadeDivisivel(int first, int last, int divisor, int threadNum)
throws InterruptedException, ExecutionException
{
int amount = 0;
- Finally, now is to test your code:
public static void main(String[] args) throws InterruptedException, ExecutionException {
System.out.println("Caso 1: " + calcularQuantidadeDivisivel(0, 100, 9) + "\n");
System.out.println("Caso 2: " + calcularQuantidadeDivisivel(1, 100, 9) + "\n");
System.out.println("Caso 3: " + calcularQuantidadeDivisivel(2, 6, 3) + "\n");
System.out.println("Caso 4: " + calcularQuantidadeDivisivel(5, 5, 3, 1) + "\n");
System.out.println("Caso 5: " + calcularQuantidadeDivisivel(5, 7, 3, 3) + "\n");
System.out.println("Caso 6: " + calcularQuantidadeDivisivel(5, 7, 3, 4) + "\n");
System.out.println("Caso 7: " + calcularQuantidadeDivisivel(8, 8, 8, 4) + "\n");
}
And see the exit:
Quantidade: 51
i: 0 threadNum: 2 primeiro: 0 ultimo: 50
i: 1 threadNum: 2 primeiro: 51 ultimo: 100
Caso 1: 12
Quantidade: 50
i: 0 threadNum: 2 primeiro: 1 ultimo: 50
i: 1 threadNum: 2 primeiro: 51 ultimo: 100
Caso 2: 11
Quantidade: 3
i: 0 threadNum: 2 primeiro: 2 ultimo: 4
i: 1 threadNum: 2 primeiro: 5 ultimo: 6
Caso 3: 2
Quantidade: 1
i: 0 threadNum: 1 primeiro: 5 ultimo: 5
Caso 4: 0
Quantidade: 1
i: 0 threadNum: 3 primeiro: 5 ultimo: 5
i: 1 threadNum: 3 primeiro: 6 ultimo: 6
i: 2 threadNum: 3 primeiro: 7 ultimo: 7
Caso 5: 1
Quantidade: 1
i: 0 threadNum: 4 primeiro: 5 ultimo: 5
i: 1 threadNum: 4 primeiro: 6 ultimo: 6
i: 2 threadNum: 4 primeiro: 7 ultimo: 7
i: 3 threadNum: 4 primeiro: 8 ultimo: 7
Caso 6: 1
Quantidade: 1
i: 0 threadNum: 4 primeiro: 8 ultimo: 8
i: 1 threadNum: 4 primeiro: 9 ultimo: 8
i: 2 threadNum: 4 primeiro: 9 ultimo: 8
i: 3 threadNum: 4 primeiro: 9 ultimo: 8
Caso 7: 1
And here’s the full code:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
public class FutureTaskDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
System.out.println("Caso 1: " + calcularQuantidadeDivisivel(0, 100, 9) + "\n");
System.out.println("Caso 2: " + calcularQuantidadeDivisivel(1, 100, 9) + "\n");
System.out.println("Caso 3: " + calcularQuantidadeDivisivel(2, 6, 3) + "\n");
System.out.println("Caso 4: " + calcularQuantidadeDivisivel(5, 5, 3, 1) + "\n");
System.out.println("Caso 5: " + calcularQuantidadeDivisivel(5, 7, 3, 3) + "\n");
System.out.println("Caso 6: " + calcularQuantidadeDivisivel(5, 7, 3, 4) + "\n");
System.out.println("Caso 7: " + calcularQuantidadeDivisivel(8, 8, 8, 4) + "\n");
}
public static int calcularQuantidadeDivisivel(int first, int last, int divisor)
throws InterruptedException, ExecutionException
{
return calcularQuantidadeDivisivel(first, last, divisor, 2);
}
public static int calcularQuantidadeDivisivel(int first, int last, int divisor, int threadNum)
throws InterruptedException, ExecutionException
{
int amount = 0;
ExecutorService executor = Executors.newFixedThreadPool(threadNum);
List<FutureTask<Integer>> taskList = new ArrayList<>(threadNum);
int quantidadeFatiasPorThread = (last - first + 1) / threadNum;
if ((last - first + 1) % threadNum != 0) quantidadeFatiasPorThread++;
System.out.println("Quantidade: " + quantidadeFatiasPorThread);
int primeiro = first;
int ultimo = primeiro + quantidadeFatiasPorThread - 1;
@SuppressWarnings("unchecked")
FutureTask<Integer>[] arrayFutureTasks = (FutureTask<Integer>[]) new FutureTask<?>[threadNum];
for (int i = 0; i < threadNum; i++) {
System.out.println("i: " + i + " threadNum: " + threadNum + " primeiro: " + primeiro + " ultimo: " + ultimo);
int primeiroFinal = primeiro;
int ultimoFinal = ultimo;
arrayFutureTasks[i] = new FutureTask<>(() -> calcularQuantidadeDivisivelSerial(primeiroFinal, ultimoFinal, divisor));
taskList.add(arrayFutureTasks[i]);
executor.execute(arrayFutureTasks[i]);
primeiro = ultimo + 1;
ultimo += quantidadeFatiasPorThread;
if (ultimo > last) ultimo = last;
}
for (int j = 0; j < threadNum; j++) {
FutureTask<Integer> futureTask = taskList.get(j);
amount += futureTask.get();
}
executor.shutdown();
return amount;
}
public static int calcularQuantidadeDivisivelSerial(int first, int last, int divisor) {
int result = 0;
for (int i = first; i <= last; i++) {
if (i % divisor == 0) result++;
}
return result;
}
}
There are still a few more improvements that I can suggest beyond that, but that remains for you to implement:
Note that in cases where there are more threads than numbers in the interval between first
and last
, excess threads end up having the value of primeiro
as being ultimo + 1
. This is not harmful as it will cause the method calcularQuantidadeDivisivelSerial
return zero, but end up creating unnecessary threads. If you want to fix this, just restrict the value of threadNum
to never be greater than last - first + 1
.
Another improvement you should make is to put the executor.shutdown();
within a block finally
with the rest of the method inside the block try
. Otherwise, if for some reason an exception occurs, your executor
will not become a kind of memory-Leak.
Validate the parameters properly: Do not leave the divisor
be zero, make sure that first <= last
and make sure that threadNum > 0
.
Thank you so much for sharing your knowledge!
– Bruno