Java 101 : What was Multithreading in old-school java like 👀?

Saurabh Kumar
3 min readFeb 12, 2022

Why do we need to read about how multithreading was handled in old-java ? So that we might know what are the pain points that the Concurrent package solves!

Let's take up a simple problem to understand this better, computing the sum of each row of a given 2D list and returning it as a 1D list.

Input : [[1,2,3],             
[4,5,6],
[7,8,9]]
Output : [6,15,24]

What is the best possible time complexity for the optimum solution that we can think of? It's bound to be O(r*c) since we need to visit each element at least once to get the sum.

Can we do better? Time complexity wise No, since there will be a reduction in the runtime of code by a constant factor due to multithreading which won't affect the time complexity of the algorithm but we can improve on the absolute runtime of the algorithm for sure!

1. No multithreading primitive version, slower!

public List<Integer> getEachRowSum(List<List<Integer>> input) {
List<Integer> result = new ArrayList<>();

for(List<Integer> list: input) {
Integer rowSum = 0;
for(Integer element: list) {
rowSum += element;
}
result.add(rowSum);

}
return result;
}

we know we can do it faster by leveraging multithreading, since there are no dependencies between the sum of each row it can be calculated independently.
i.e the code portion in bold fonts in the previous example can be executed in parallel for each row, where each thread is only concerned with its own row and updates the calculated value in the result after the operation is done.

What are the most basic abstractions that we can use for writing multithreaded code in java?
Thread and Runnable combination,

public interface Runnable { 
public void run();
}

No Runnable is not a thread or create a thread, It is just a simple functional interface with a method run(). It depicts a task that can be run on a thread

public class Thread extends Object implements Runnable

The Thread Class abstraction in java maps to an actual bare thread in OS which can take up tasks to execute in the form of a concrete runnable instance.

2. Multithreaded variant of the solution, faster.

public List<Integer> getEachRowSum(List<List<Integer>> input) {

List<Integer> result = new
ArrayList<(Collections.nCopies(input.size(), 0));

for(Integer i = 0; i<input.size(); i++) {
final Integer index = i;
Thread t = new Thread(new Runnable() {
public void run() {

Integer rowSum = 0;
for(Integer element: input.get(index)) {
rowSum+= element;
}
result.set(index, rowSum);
}
});

// Starting this particular thread
t.start();
try {
// puts the main thread on wait untill
// execution of thread t is complete
t.join();
} catch(Exception e) {
// dont do anything
}
}
return result;
}

Why do we need the whole concurrent package, if this Runnable Thread framework was working fine?
Because there were some issues with using Runnable and Thread for writing multithreaded code in Java 👀

  1. Increasing the number of threads should help us solve problems faster right? No, not really it can even result in an OOM error if we create way too many threads.
    Even if we are able to create so many threads, the performance might become worse due to Thrashing.
  2. What if we wanted to share some resources across threads? i.e how will we handle the race conditions without the synchronized keyword which is part of the concurrent package? boolean flags, wait, and notify are just too complex in comparison to the simple synchronized keyword!
  3. Need to handle all the checked exceptions inside the runnable, cannot propagate it to the caller.
  4. Cannot return any value from the task i.e the runnable that was executed.

All these problems and in addition some more problems have been solved by the introduction of the concurrent package so that we can focus more on the solution rather than thread management. 🥳
written more about this article

leave me a clap if you found this article insightful 😄, Happy Reading !

--

--