0

I have been following several YouTube demos and tutorials on implementing multi-threaded operations in Java. However, all the tutorials show this following procedure:

class Task implements Runnable {
    @Override
    public void run() {
        doTask();
    }

    public void doTask() {
        for (int i = 0; i < 1500; i++) {
            System.out.print('T');
        }
    }
}

public class Main {

    public static void main(String[] args) {
        Task t = new Task();

        Thread thread = new Thread(t);
        thread.start();

        for (int i = 0; i < 1500; i++) {
            System.out.print('M');
        }
    }
}

Some demos extends Thread class instead of Runnable, however, they follow a similar concept.

However, one problem that I reckon is that what if I want to have multiple logics in a class that I want to run concurrently, then, I have a issue. Well, Java veterans may know some trick to do so. However, I tried to implement such logic in a different way. Here is the code that I wrote:

class Task {
    public void doTask() {
        for (int i = 0; i < 1500; i++) {
            System.out.print('T');
        }
    }
}

public class Main {

    public static void main(String[] args) {
        Task t = new Task();

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                t.doTask();
            }
        });
        thread.start();

        for (int i = 0; i < 1500; i++) {
            System.out.print('M');
        }
    }
}

Now, I can launch several threads and call the particular method that I want run concurrently.

Now, I want to know is there any benefit of implementing multi threaded operation using the first approach and is there any shortcomings of the latter one?

4 Answers4

3

There is no point in defining a Runnable class with a run() method that only invokes doTask. Just put the contents of doTask in run.

Or, better, use a lambda:

Thread thread = new Thread(() -> {
  // Stuff you want to do.
});

But there is absolutely no good reason to extend Thread.

In fact, in many cases you don't want to use Thread directly: instead, use an ExecutorService, to which you submit Runnable/Callable instances.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • 1
    You could also use method references (using the `Task` class of the OP as example): `Thread thread = new Thread(t::doTask);` – Thomas Kläger Mar 13 '21 at 09:59
2

The Answer by Andy Turner is correct. I just want to emphasize that in modern Java there is rarely any reason to directly address the Thread class. The tutorials you read are seriously outmoded; ignore them.

Executor services

The executor service framework was added to Java to abstract away the chores of thread management.

Look to the Executors class to instantiate various implementations of ExecutorService.

ExecutorService es = Executors. … ;

Submit your Runnable Or Callable object to the executor service.

es.submit( new MyRunnable() ) ;

If you want to track the completion of your task, capture the Future object returned by the submit method.

Project Loom is bringing a new executor service that uses lightweight virtual threads (fibers) to use less memory, execute faster, and make blocking very cheap, to allow for millions of threads running.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
0

What is best way of implementing multithreading in java?

There is no single "best" way.

There are different ways ... that have advantages and disadvantages.

The simple way is to use one of the standard ExecutorService implementations and submit your tasks as lambdas or instances of Runnable or Callable.

ForkJoin pools are similar, but tuned for a different kind of task where one task forks multiple subtasks and then joins them; e.g. divide and conquer algorithms.

Another approach is to use a 3rd-party thread pool; e.g. as provided by Guava.

If you really need to manage your own threads you could implement your own thread pool, but there is probably something "out there" that provided the functionality you need.

If you have a task that needs to be run periodically (in a thread), look at TimerTask or a more sophisticated task scheduling library; e.g. Quartz.

If you have a situation where the threads are static and long running, you can instantiate and start the Thread objects directly, passing the body of the thread as a Runnable (or lambda).

Just about the only thing to avoid is writing a class that extends the Thread class. It is a bad idea. And if you come across an example or demos that shows does this, ignore it. Extending Thread embeds your business logic in the thread's run() method. This makes it difficult to adapt your code to use a different thread management strategy.

Recommended reading:


Finally, there is an Java incubator project ("Loom") that is developing a light-weight thread model. These threads are called "fibres". It is probably not relevant to you now.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
0

In your simple example there is absolutely no downside (or upside) to using a shared object. But you can run into trouble if you have an mutable variable in your object that is changed in multiple tasks.

class Task {
    //Getter
    private long taskId;

    public void doTask() {
        taskId++;
        for (int i = 0; i < 1500; i++) {
            System.out.print('T');
        }
    }
}

In this case you would have to use the synchronized keyword or AtomicLong. Otherwise you may end up in a race condition and taskId gives wrong values.

magicmn
  • 1,787
  • 7
  • 15