0

I'm learning executor service in java. Requirement is using executor service create 4 threads -

Thread 1 - get two numbers as input.

Thread 2 - addition of two numbers

Thread 3 - multiplication of two numbers

Thread 4 - print the results.

Thread 1 should be executed first, after thread 1 is complete thread 2, 3 can start processing simultaneously and finally once thread 2, 3 is completed. Thread 4 should run finally.

How can we make sure which thread starts first and which threads to start simultaneously next. Finally which thread to execute at last.

Note: How can we achieve this without using thread sleep. Dynamically as soon as thread 1 finishes other two threads should start automatically.

  • Thread 1 could start Thread 2 and 3(and maybe Thread 4). Thread 4 could join all Threads. – dan1st Feb 02 '20 at 17:14
  • What have you tried and what doesn't work with it? – daniu Feb 02 '20 at 17:21
  • 1
    FYI, This exercise might teach you something about _how_ to use threads, but it is the absolutely worst example of _why_ to use threads. It's like, somebody is teaching you how to operate a car by having you use the car's front bumper to ram the nails home in a piece of furniture that you are building. I'm serious. This example is _that_ bad. If a program needs to do certain things in a certain order, the only right way to do that is to do all of those things in a single thread. The entire point of threads is to enable you to do things _concurrently_ (which means, in no particular order.) – Solomon Slow Feb 02 '20 at 18:00
  • 1
    Also note: Don't think of executor service as a way to create threads. Think of it as a way to _perform background tasks._ The whole point of an executor service is to shield you, as much as possible, from having to think about the "worker threads" that it uses to perform your tasks. – Solomon Slow Feb 02 '20 at 21:34

2 Answers2

2

First, Read my comment on your original question--the one about using a car to hammer in nails.

Ok, Now, @dan1st had some ideas about how to structure the solution. Here's two more.

  1. Use a global int variable, a global lock, and wait() and notifyAll(): Have each thread enter a synchronized(lock) block in which it

    • Loops, calling lock.wait() until the global int has some particular value,
    • Does its trick,
    • Sets the global int to the value that will trigger the next thread,
    • calls lock.notify(), and finally
    • exits
  2. Use Semaphores: Pass two Semaphore instances in to each thread. Call them in and out or some such names. Have each thread

    • wait its turn by calling in.acquire(),
    • do its trick,
    • call out.release(),
    • and then exit.

Your main routine then is responsible for creating the semaphores, and passing them to the new threads in such a way that each thread's out refers to the same semaphore as the in of the thread that is expected to perform the subsequent task.


IMO, option 2 is more elegant, because if your teacher asks you next week to modify your program by adding another step in the middle of the sequence, then none of the existing tasks that you wrote will have to change at all. You'll only need to write the new task and change two or three lines in the main() routine.

Doesn't sound like much of an improvement, and option 2 clearly is more work to set up in the first place than option 1, but if you ever are employed to work on enterprise-scale software systems with millions of lines of code, you will come to appreciate the beauty of option 2.

Solomon Slow
  • 25,130
  • 5
  • 37
  • 57
  • 2
    P.S., If I ever teach a programming class, my students will learn real quick to expect, "now, change the program you wrote last week so that..." Out in the real world, requirements change. Often. One of the most important skills a developer can learn is how to write code that you don't have to throw away and start from scratch every time the requirements change. – Solomon Slow Feb 02 '20 at 18:43
1

You could do this using multiple ways.

For exanple, joining:

A Thread can join another Thread, which means that it waits until the other Thread finishes. Thread 2 and 3 could join Thread 1 and Thread 4 could join Thread 2 and 3.

Another possibility is await and signal but I am not sure if it meets your Requirements(it uses something similar to Thread.sleep():

At first, you create a common java.util.concurrent.locks.Lock and create a condition of this lock with .newCondition() You also create a second condition using newCondition().

The lock has to be locked before calling await and signal/signalAll.

Thread 2 and 3 calls .await() on the first condition before starting and Thread 1 calls .signalAll on the first condition when it finishes.

Thread 4 calls .await() on the second condition before it starts.

The Thread (either 2 or 3) that finishes last(the logic which Thread finished first should be synchronized with the lock) calls .signal() on the second condition.

The threads could also start each other:

Thread 1 starts Thread 2 and 3 after it's task finishes but I would recommand you one of the other mechanisms for Thread 4.

[DISCLAIMER]

You may not be able to interact with the Threads directly if you use an ExecutorService. This post might help you with joining, await/signal should not be more difficult and the Threads can also schedule a task to the thread pool if needed.

dan1st
  • 12,568
  • 8
  • 34
  • 67
  • Thanks a lot. I understood your idea in basic java thread concept. Is this concept actually same in java 8 also..? –  Feb 02 '20 at 17:29