简体   繁体   中英

DIfference between new Thread(Runnable object).start() vs. new Thread(Runnable ref. {run()});.start()

I am a newie in multithreading. I have following codes for two java program

public class Test implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("i am Thread-" +  Thread.currentThread().getName());
}
    }
    public static void main(String[] args) {
        Test t = new Test();
        new Thread(t).start();
       System.out.println("i am Thread-" +  Thread.currentThread().getName());
}}
---------------------------------------------
public class Test1 {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 5; i++) {
                    System.out.println("i am Thread-"+ Thread.currentThread().getName());
                }}});
        t1.start();
        System.out.println("i am Thread-"+ Thread.currentThread().getName());

        }}
--------------------------------------------

As both gives the same output. Could someone please explain what is the actual difference between thsese two approaches. Thankyou.

Named class, anonymous class, and lambda syntax

  • The first is an explicitly-named class.
  • The second is an anonymous class .

No practical difference in your example code. Use whichever approach meets your needs and taste. Generally, if the class will be reused in various places, then using a named class makes sense. If the class is brief and used in just one place, then the anonymous class approach may be better.

As commented by Andreas , one big advantage of the anonymous class is that it can access any effectively final variables contained in its outer nesting method. This can simplify your code by eliminating the chore of communicating values back and forth. But beware: In your case of doing concurrent work you would need to protect any such variables against non-thread-safe access.

A third approach is using lambda syntax.

Runnable task = 
    () -> {   
         for ( int i = 1 ; i <= 5 ; i++ ) {
             System.out.println( "Thread ID: " + Thread.currentThread().getId() + " running at " + Instant.now() );
         }
    }
;

Executors framework

By the way, in modern Java we generally have no need to directly address the Thread class. Instead, we use the Executors framework.

Below is an example using anonymous class.

Tip: Threads do not always have a name, so using their ID is more reliable.

ExecutorService es = Executors.newCachedThreadPool() ;

Runnable task = 
    new Runnable() {  // Anonymous class.
         @Override
         public void run() {
            for ( int i = 1 ; i <= 5 ; i++ ) {
                System.out.println( "Thread ID: " + Thread.currentThread().getId() + " running at " + Instant.now() );
             }
         }
    }
;

es.submit( task ) ;
es.submit( task ) ; 
es.submit( task ) ;

es.shutdown() ;
es.awaitTermination( 1 , TimeUnit.MINUTES ) ; 

Same thing, but using the more compact lambda syntax.

ExecutorService es = Executors.newCachedThreadPool() ;

Runnable task = 
    () -> {   // Lambda syntax.
         for ( int i = 1 ; i <= 5 ; i++ ) {
             System.out.println( "Thread ID: " + Thread.currentThread().getId() + " running at " + Instant.now() );
         }
    }
;

es.submit( task ) ;
es.submit( task ) ; 
es.submit( task ) ;

es.shutdown() ;
es.awaitTermination( 1 , TimeUnit.MINUTES ) ; 

Full example code

For a fully-functioning example, add:

  • Random amount of sleep for realism.
  • Needed exception-handling.
  • Test for exceeding our time-out when waiting for submitted tasks to finish.
package work.basil.demo.concurrent;

import java.time.*;
import java.util.concurrent.*;

public class App
{
    public static void main ( final String[] args )
    {
        final ExecutorService es = Executors.newCachedThreadPool();

        final Runnable task =
                ( ) -> {   // Lambda syntax.
                    for ( int i = 1 ; i <= 5 ; i++ )
                    {
                        try { Thread.sleep( ThreadLocalRandom.current().nextInt( 10 , 200 ) ); } catch ( final InterruptedException e ) { e.printStackTrace(); }
                        System.out.println( "Thread ID: " + Thread.currentThread().getId() + " running nth time: " + i + " at " + Instant.now() );
                    }
                };

        es.submit( task );
        es.submit( task );
        es.submit( task );

        es.shutdown();
        try
        {
            if ( ! es.awaitTermination( 1 , TimeUnit.MINUTES ) )
            {
                System.out.println( "WARN - Executor service time-out expired. Message # 0c75c656-f6bb-4454-9cdd-360caedc555d" );
            }
        }
        catch ( final InterruptedException e )
        {
            e.printStackTrace();
        }
    }
}

When run.

Thread ID: 14 running nth time: 1 at 2021-04-24T06:41:31.758156Z
Thread ID: 16 running nth time: 1 at 2021-04-24T06:41:31.903004Z
Thread ID: 16 running nth time: 2 at 2021-04-24T06:41:31.916130Z
Thread ID: 14 running nth time: 2 at 2021-04-24T06:41:31.916130Z
Thread ID: 15 running nth time: 1 at 2021-04-24T06:41:31.937443Z
Thread ID: 14 running nth time: 3 at 2021-04-24T06:41:31.949898Z
Thread ID: 15 running nth time: 2 at 2021-04-24T06:41:31.957326Z
Thread ID: 15 running nth time: 3 at 2021-04-24T06:41:31.975608Z
Thread ID: 16 running nth time: 3 at 2021-04-24T06:41:32.016034Z
Thread ID: 16 running nth time: 4 at 2021-04-24T06:41:32.085384Z
Thread ID: 16 running nth time: 5 at 2021-04-24T06:41:32.123679Z
Thread ID: 15 running nth time: 4 at 2021-04-24T06:41:32.134960Z
Thread ID: 14 running nth time: 4 at 2021-04-24T06:41:32.143010Z
Thread ID: 15 running nth time: 5 at 2021-04-24T06:41:32.283166Z
Thread ID: 14 running nth time: 5 at 2021-04-24T06:41:32.325071Z

You have already noted that both programs produce the same output and have asked for the differences in those two approaches, and here is what I see the distinction between the two.

1. Open to extension via top-level class

By implementing Runnable interface in a top-level class, you are allowing the code to be extended or reused by other classes, whereas anonymous class approach does not let you extend outside the class. One may ask, "why would anyone try to reuse run() method from a parent class?", and that's a fair question but there may be other reasons to extend such class.

2. Access to surrounding context via anonymous class

In contract, anonymous class approach lets you access final and effective final variables as well as methods, as the case may be, of the surrounding context. There is no such equivalent surrounding context for a top-level class and only way to pass more information would be say, via constructor or other init mechanism before starting the Thread .

3. Option of using Lambda expression

Finally, there is the option of converting an anonymous class into a lambda expression and make code more succinct and therefore more readable. I'm not saying that top-level classes aren't readable but given the choice between an inline & short Lambda expression and having to navigate to another class, I'd prefer the former.

To summarise: top-level class approach lets the code to be extended, whereas the anonymous class (or lambda) approach lets the code access the surrounding context concisely.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM