0

I have a txt file: order_me.txt, in which there are some integers which need to be sorted using 4 threads. They need to work simultaneously, but not do the same thing. I have managed to sort the integers, but something is not working right...

This is the thread class:

public class Threading {

    static List<Integer> integersCopy = new ArrayList<>();

    public static void main(String[] args) {
        openFile();
        Thread t1 = new Thread(new Command("thread 1", integersCopy));
        t1.start();

        Thread t2 = new Thread(new Command("thread 2", integersCopy));
        t2.start();

        Thread t3 = new Thread(new Command("thread 3", integersCopy));
        t3.start();

        Thread t4 = new Thread(new Command("thread 4", integersCopy));
        t4.start();

        try {
            if (t1.isAlive())
                t1.join();
            if (t2.isAlive())
                t2.join();
            if (t3.isAlive())
                t3.join();
            if (t4.isAlive())
                t4.join();
        } catch (Exception e) {
            System.out.println("Exception with threads");
        }
    }

    public static void openFile() {
        File file = new File("order_me.txt");
        try {
            Scanner scanner = new Scanner(file);
            List<Integer> integers = new ArrayList<>();
            while (scanner.hasNext()) {
                if (scanner.hasNextInt()) {
                    integers.add(scanner.nextInt());
                } else {
                    scanner.next();
                }
            }
            integersCopy = integers;
            System.out.println("File opened successfully");

        } catch (Exception e) {
            System.out.println("Triggered exception");
        }
    }

And this is the sorting class:

    import java.util.Collections;
    import java.util.List;

    public class Command implements Runnable {
    String threadName;
    List<Integer> listOfInts;

    Command(String name, List<Integer> list) {
        threadName = name;
        listOfInts = list;
    }

    @Override
    public void run() {
        for (int i = 0; i < listOfInts.size(); i++) {
            Collections.sort(listOfInts);
            System.out.print(+listOfInts.get(i) + " ");
        }
    }
}
Hülya
  • 3,353
  • 2
  • 12
  • 19
Iuli
  • 31
  • 1
  • 8
  • 1
    " but something is not working right" We're definitely going to need more detail to be able to help you. What is not working right? Do you get an error message? An unexpected output? – mypetlion Nov 01 '18 at 16:33
  • You need to give us much more context than "something is not working right". Otherwise, it's going to be pretty difficult to help you out. Any concrete errors you're getting? Actual output that's not sorted correctly? – mjuarez Nov 01 '18 at 16:34
  • For a start though, I'll point out to you that you're sorting the same list over and over again. Once for each element in the list. Put the `Collections.sort` call outside of the loop. – mypetlion Nov 01 '18 at 16:36
  • @mjuarez,@mypetlion: This is the result: Exception in thread "Thread-0" Exception in thread "Thread-3" Exception in thread "Thread-1" java.util.ConcurrentModificationException. The numbers were sorted ok without threads, but they sort the numbers like: 1 1 3 5 6 6. Original txt file has the numbers:5 4 7 1 6 3 – Iuli Nov 01 '18 at 16:37
  • If I only let a thread inside the program, it is working just fine. If I add threads, it throws exceptions. – Iuli Nov 01 '18 at 16:41

2 Answers2

2

Once one thread has sorted the integers there is no point trying to do the same thing in multiple threads, in fact as this is not done in a thread safe manner you are likely to corrupt the list.

In short, use one thread.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
0

You are mutating a shared List on several thread. ArrayList are not thread safe: you need to either use a thread safe Collection or use Collections.synchronizedList.

And if you are sorting the same list in each thread, then you are probably wrong in your implementation:

  • You should read the list once in the parent thread
  • You should split the list by the number of thread and sort each sublist in each own thread. That is the purpose of Thread in such case: dividing the work to do.
  • You should then join sub list in the parent thread: during the join process, you will have to sort. Since each sublist is sorted you may use a faster sort (eg: if the first item of sublist A if after last of sublist B, then you may add all items of A into B) or the standard sort.

Also, you may use Stream to do that:

  • The parallel() method is what make the Stream use several threads.
  • ForkJoinPool is used to use another thread pool (see this SO answer). It may be useless for an example.
  • Sadly, Scanner does not translate into IntStream or Stream. You need to use Files.lines and a Pattern to split line by whitespace (\s+).
  • We use flatMapToInt to convert to a IntStream the OptionalInt (it will be empty for invalid number, ex: *a4).
  • sorted() ensure that we sort using the default sort. Sorting using a comparator would require a normal Stream and thus, that flatMapToInt be changed to flatMap.
  • toArray() is probably better in that case than using ArrayList of Integer as in second example.

Code tested with Java 11:

  public static void main(final String[] args) throws Exception {
    final Pattern splitter = Pattern.compile("\\s+");
    // see
    // 
    final ForkJoinPool forkJoinPool = new ForkJoinPool();
    final Future<int[]> future = forkJoinPool.submit(() -> {
      try (final Stream<String> lines = Files.lines(Paths.get("myfile.txt"), StandardCharsets.UTF_8)) {
        return lines.parallel() // use default parallel ExecutorService
            .flatMap(splitter::splitAsStream) // tokenize input
            .flatMapToInt(s -> parseInt(s).stream()) // convert to number (eg: nextInt())
            .sorted().toArray();
      }
    });

    final int[] result = future.get();
    System.out.println("result.length: " + result.length);
    // Arrays.stream(result).forEach(System.out::println);
    final Future<List<Integer>> future2 = forkJoinPool.submit(() -> {
      try (Stream<String> lines = Files.lines(Paths.get("myfile.txt"), StandardCharsets.UTF_8)) {
        return lines.parallel() // use default parallel ExecutorService
            .flatMap(splitter::splitAsStream) // tokenize input
            .flatMapToInt(s -> parseInt(s).stream()) // convert to number (eg: nextInt())
            .sorted().collect(ArrayList::new, List::add, List::addAll) // probably best to use
                                                                       // array.
        ;
      }
    });
    final List<Integer> result2 = future2.get();
    System.out.println("result2.length: " + result2.size());
  }

  static OptionalInt parseInt(final String s) {
    try {
      return OptionalInt.of(Integer.parseInt(s));
    } catch (final NumberFormatException e) {
      return OptionalInt.empty();
    }
  }
NoDataFound
  • 11,381
  • 33
  • 59