1

I need to make a test - which of the lists is faster. To do this, I run each test in a new thread. I create the runnable method for the each thread in a separate class. I get a rather monotonous code, this is a DRY violation. Maybe someone knows how I can run all threads at the same time without breaking DRY?

public class ThreadsManager {

    public static boolean stopThreads = false; //We change this variable MainActivity
    public static long timeOfStartTest = 0;

    public void startCollThreads() {
        stopThreads = false;
        timeOfStartTest = new Date().getTime();
        CollRunnable collRunnable = new CollRunnable();

        new Thread(collRunnable.addBeg(new ArrayList())).start();
        new Thread(collRunnable.addBeg(new LinkedList())).start();
        new Thread(collRunnable.addBeg(new CopyOnWriteArrayList())).start();

        new Thread(collRunnable.addMid(new ArrayList())).start();
        new Thread(collRunnable.addMid(new LinkedList())).start();
        new Thread(collRunnable.addMid(new CopyOnWriteArrayList())).start();

        new Thread(collRunnable.addEnd(new ArrayList())).start();
        new Thread(collRunnable.addEnd(new LinkedList())).start();
        new Thread(collRunnable.addEnd(new CopyOnWriteArrayList())).start();

        new Thread(collRunnable.search(new ArrayList())).start();
        new Thread(collRunnable.search(new LinkedList())).start();
        new Thread(collRunnable.search(new CopyOnWriteArrayList())).start();

        new Thread(collRunnable.removeBeg(new ArrayList())).start();
        new Thread(collRunnable.removeBeg(new LinkedList())).start();
        new Thread(collRunnable.removeBeg(new CopyOnWriteArrayList())).start();

        new Thread(collRunnable.removeMid(new ArrayList())).start();
        new Thread(collRunnable.removeMid(new LinkedList())).start();
        new Thread(collRunnable.removeMid(new CopyOnWriteArrayList())).start();

        new Thread(collRunnable.removeEnd(new ArrayList())).start();
        new Thread(collRunnable.removeEnd(new LinkedList())).start();
        new Thread(collRunnable.removeEnd(new CopyOnWriteArrayList())).start();
    }
}
public class CollRunnable {

    //region Adding in the beginning
    public Runnable addBeg(List list) {
        return () -> {
            for (int i = 0; i <= elementsNumber; i += 1) {
                if (stopThreads) {
                    return;
                }
                list.add(0, "");
            }
            updateViewModelValue(1,"Adding \nin the beginning", list);
        };
    }
    //endregion

    //region Adding in the middle
    public Runnable addMid(List list) {
        return () -> {
            for (int i = 0; i <= elementsNumber; i += 1) {
                if (stopThreads) {
                    return;
                }
                list.add(0, "");
            }
            updateViewModelValue(2, "Adding \nin the middle", list);
        };
    }
    //endregion

    //region Adding in the end
    public Runnable addEnd(List list) {
        return () -> {
            for (int i = 0; i <= elementsNumber; i += 1) {
                if (stopThreads) {
                    return;
                }
                list.add("");
            }
            updateViewModelValue(3, "Adding \nin the end", list);
        };
    }
    //endregion

    //region Search
    public Runnable search(List list) {
        return () -> {
            list.add(0);

            for (int i = 0; i <= elementsNumber; i += 1) {
                if (stopThreads) {
                    return;
                }
                list.get(0);
            }
            updateViewModelValue(4, "Search \nby value", list);
        };
    }
    //endregion

    //region Removing in the beginning
    public Runnable removeBeg(List list) {
        return () -> {
            for (int i = 0; i <= elementsNumber; i += 1) {
                if (stopThreads) {
                    return;
                }
                list.add("");
                list.remove("");
            }
            updateViewModelValue(5, "Removing \nin the beginning", list);
        };
    }
    //endregion

    //region Removing in the middle
    public Runnable removeMid(List list) {
        return () -> {
            for (int i = 0; i <= elementsNumber; i += 1) {
                if (stopThreads) {
                    return;
                }
                list.add("");
                list.remove("");
            }
            updateViewModelValue(6, "Removing \nin the middle", list);
        };
    }
    //endregion

    //region removing in the end
    public Runnable removeEnd(List list) {
        return () -> {
            for (int i = 0; i <= elementsNumber; i += 1) {
                if (stopThreads) {
                    return;
                }
                list.add("");
                list.remove("");
            }
            updateViewModelValue(7, "Removing \nin the end", list);
        };
    }
    //endregion

    private void updateViewModelValue(int numberOfTest, String testName, List list) {
        new Handler(Looper.getMainLooper()).post(() -> {
            DataClass dataClass = new DataClass(numberOfTest,testName, list, new Date().getTime() - timeOfStartTest);
            getCollInstance().viewModel.testResult.setValue(dataClass);
        });
    }
}

dreamcrash
  • 47,137
  • 25
  • 94
  • 117
Paul Maltsev
  • 452
  • 1
  • 5
  • 14
  • Just for the record: starting many threads and checking which one finishes first is **not** a sensible way of benchmarking various approaches to a problem, you'll want to write a proper micro-benchmark, which [can be tricky](https://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java). – Joachim Sauer Apr 03 '21 at 17:44

3 Answers3

1

One way would be to detach the creation of the list. For example with the help of an interface:

@FunctionalInterface
public interface ListFactory {
    List create();
}

    // In ThreadsManager
    public void orquestate() {
        startCollThreads(() -> new ArrayList());
        startCollThreads(() -> new LinkedList());
        startCollThreads(() -> new CopyOnWriteArrayList());
    }

    private void startCollThreads(ListFactory listFactory) {
        stopThreads = false;
        timeOfStartTest = new Date().getTime();
        CollRunnable collRunnable = new CollRunnable();

        new Thread(collRunnable.addBeg(listFactory.create())).start();
        new Thread(collRunnable.addMid(listFactory.create())).start();
        // Other steps
    }

It's not exactly the same as the steps are started first for lists types, but I don't know if that is important for you.

polypiel
  • 2,321
  • 1
  • 19
  • 27
1

You can take advantage of the Supplier functional interface. As follows, extract a method that will test a given function passed as parameter on the list types:

private void run_test(Function<List, Runnable> collRunnable) {
    new Thread(collRunnable.apply(new ArrayList())).start();
    new Thread(collRunnable.apply(new LinkedList())).start();
    new Thread(collRunnable.apply(new CopyOnWriteArrayList())).start();
}

and then adapt your startCollThreads method:

public void startCollThreads() {
    stopThreads = false;
    timeOfStartTest = new Date().getTime();
    CollRunnable collRunnable = new CollRunnable();

    run_test(collRunnable::addBeg);
    run_test(collRunnable::addMid);
    run_test(collRunnable::addEnd);
    run_test(collRunnable::search);
    run_test(collRunnable::removeBeg);
    run_test(collRunnable::removeMid);
    run_test(collRunnable::removeEnd);
}

A side note

You are using Raw types, you should avoid that, see this source to see why.

You can make it more generic by also parameterize the list passed as parameter:

private void run_test(Function<List, Runnable> collRunnable, Supplier<List>... lists) {
    for (Supplier<List> list : lists) {
        new Thread(collRunnable.apply(list.get())).start();
    }
}

then you can called the method with different lists:

run_test(collRunnable::addBeg, ArrayList::new, LinkedList::new, CopyOnWriteArrayList::new);

or

run_test(collRunnable::addBeg, ArrayList::new);
dreamcrash
  • 47,137
  • 25
  • 94
  • 117
1

You can use thread pool API for this propose. For example ExecutorService.html.invokeAll

For example:

   try {
       Runnable routine = (List<?> args) -> { // add your thread routine implementation }
       // you can use same routine for all tasks, or make any combination such as 3 different routines in list etc
       Runnable[] allTasks = new Runnable[8];
       Arrays.fill(allTasks, routine);
       ExecutorService pool =  Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
       // invoke all tasks from array in thread pool
       pool.invokeAll( Arrays.asList(allTasks) );
       // stop pool and await for it termination
       pool.shutdown();
       if (!pool.awaitTermination(60, TimeUnit.MINUTES)) {
         pool.shutdownNow();
       }
     }
   } catch (InterruptedException ie) {
     pool.shutdownNow();
     Thread.currentThread().interrupt();
   }
Victor Gubin
  • 2,782
  • 10
  • 24