I am working on Thread for the first time and I tried to code an example I saw on the internet. An ArrayList of numbers must be divided into 4 parts, and 4 separate threads need to find the odd and even numbers in those parts and add them to the "evens" or "odds" list. Although I do not have any problems with the algorithm, I have problems with Threads.
Since the codes are not very long, I am adding them completely.
My Runnable Class:
package ThreadRace;
public class OddEvenFinder implements Runnable {
private final int id;
private final int size;
public OddEvenFinder(int id, int size) {
this.id = id;
this.size = size;
}
@Override
public void run() {
int start = id * this.size;
int end = start this.size;
while (start < end) {
if (Starter.numbers.get(start) % 2 == 0) {
Starter.evens.add(start);
}
else {
Starter.odds.add(start);
}
start ;
}
}
}
My testing class:
package ThreadRace;
import java.util.ArrayList;
import java.util.List;
public class Starter {
public static List<Integer> numbers = new ArrayList<>();
public static List<Integer> evens = new ArrayList<>();
public static List<Integer> odds = new ArrayList<>();
public static void main(String[] args) throws InterruptedException {
for (int i = 1; i <= 10000; i ) {
numbers.add(i);
}
OddEvenFinder f1 = new OddEvenFinder(0, numbers.size() / 4);
OddEvenFinder f2 = new OddEvenFinder(1, numbers.size() / 4);
OddEvenFinder f3 = new OddEvenFinder(2, numbers.size() / 4);
OddEvenFinder f4 = new OddEvenFinder(3, numbers.size() / 4);
Thread thread1 = new Thread(f1);
Thread thread2 = new Thread(f2);
Thread thread3 = new Thread(f3);
Thread thread4 = new Thread(f4);
thread1.start();
thread2.start();
thread3.start();
thread4.start();
thread1.join();
thread2.join();
thread3.join();
thread4.join();
System.out.println(evens.size());
System.out.println(odds.size());
}
}
When I run the application this way, the length of the evens and odds lists should be 5000-5000, but I get a result between 3000-4000.
Shouldn't the .join() function wait for threads to finish? How can there be numbers that are not included in the lists?
The interesting part is that the problem is almost resolved when I add a few words to debug.
When I edit the code like this:
@Override
public void run() {
int start = id * this.size;
int end = start this.size;
while (start < end) {
System.out.println("Thread number " (this.id 1) " is working");
if (Starter.numbers.get(start) % 2 == 0) {
System.out.println(start " added to evens");
Starter.evens.add(start);
}
else {
System.out.println(start " added to odds");
Starter.odds.add(start);
}
start ;
}
}
The output I get gives almost accurate results like 4999-5000. When I set the size of the numbers array to a smaller value such as 4000-5000, it gives the correct result.
I have 2 questions:
1- Why .join() is not working or what am I wrong about .join()?
2- How is it that printing a few texts makes the program run more accurately?
CodePudding user response:
In the JavaDocs of ArrayList, it says in bold "Note that this implementation is not synchronised". So if several threads want to add an element at the same time, only the last call of the method will set the real value. The values of the other threads are simply overwritten. Therefore, you will get fewer numbers than expected. In order for the list to be filled in a synchronised way, you should use the keyword "synchronized" as shown below.
synchronized (Starter.evens) {
Starter.evens.add(start);
}
and
synchronized (Starter.odds) {
Starter.odds.add(start);
}