Java
Anonymous Classes
Runnable Interface
Multithreading
Software Development

Best way of creating and using an anonymous Runnable class

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

An anonymous Runnable class is a compact way to define short thread work inline, but it is not always the best abstraction. In modern Java, the practical answer is: use an anonymous class only for small, local behavior, prefer lambdas when available, and prefer executors over manually starting raw threads in application code.

Classic Anonymous Runnable

The traditional pattern looks like this:

java
1Thread worker = new Thread(new Runnable() {
2    @Override
3    public void run() {
4        System.out.println("running in background");
5    }
6});
7
8worker.start();

This is valid Java and still useful when:

  • the task is tiny
  • it is used only once
  • you want the behavior next to the call site

The main benefit is locality. You do not have to create a separate named class for a throwaway task.

Prefer a Lambda in Modern Java

Because Runnable is a functional interface, Java 8 and later let you replace the anonymous class with a lambda.

java
1Thread worker = new Thread(() -> {
2    System.out.println("running in background");
3});
4
5worker.start();

This is usually clearer and shorter.

So if the question is "best way" in current Java, the answer is often: do not use an anonymous class at all if a lambda expresses the same thing more cleanly.

Raw Threads Are Not Always the Best Choice

The bigger design issue is often not anonymous class versus lambda. It is whether you should create a Thread directly.

In most server or application code, prefer an ExecutorService.

java
1import java.util.concurrent.ExecutorService;
2import java.util.concurrent.Executors;
3
4ExecutorService pool = Executors.newFixedThreadPool(2);
5
6pool.submit(() -> {
7    System.out.println("task running in executor");
8});
9
10pool.shutdown();

This is better than creating many raw threads manually because:

  • thread reuse is built in
  • resource limits are easier to control
  • shutdown behavior is more manageable

That is the more important best practice.

Keep Anonymous Tasks Small

If the body grows large, inline code becomes harder to read and test.

Bad direction:

java
new Thread(() -> {
    // 50 lines of business logic
}).start();

Once the runnable contains real business rules, extract it into a named method or a named class.

Cleaner:

java
ExecutorService pool = Executors.newSingleThreadExecutor();
pool.submit(this::processOrders);

That keeps concurrency wiring separate from application logic.

Captured Variables

Anonymous classes and lambdas can capture values from the surrounding scope, but only if those values are effectively final.

java
1String jobName = "sync-users";
2
3Thread worker = new Thread(() -> {
4    System.out.println("starting " + jobName);
5});
6
7worker.start();

This is useful, but avoid capturing large mutable objects casually. The more state the runnable depends on, the harder it becomes to reason about thread safety.

Naming and Error Handling

If you do create a raw thread, name it and think about exception handling.

java
1Thread worker = new Thread(() -> {
2    try {
3        System.out.println("doing work");
4    } catch (Exception ex) {
5        ex.printStackTrace();
6    }
7}, "background-worker");
8
9worker.start();

Unnamed threads and silent failures make debugging much harder.

When using executors, also think about how task exceptions are observed. Fire-and-forget concurrency is where many production bugs hide.

When an Anonymous Class Is Still Useful

Anonymous inner classes still make sense when you need features lambdas do not emphasize as clearly, such as:

  • a custom class body with more than one method or field
  • compatibility with older Java versions
  • explicit type identity in legacy APIs

But for a plain Runnable, a lambda is almost always the cleaner modern form.

Common Pitfalls

  • Treating anonymous Runnable classes as the default choice in Java 8 and later.
  • Putting too much business logic directly inside an inline task body.
  • Starting raw threads where an executor would be more appropriate.
  • Capturing mutable state without thinking about thread safety.
  • Ignoring thread naming and exception visibility.

Summary

  • Anonymous Runnable classes are valid, but they are best for short local tasks.
  • In modern Java, lambdas are usually the cleaner syntax for Runnable.
  • Prefer executors over manually creating raw threads for most application workloads.
  • Keep task bodies small and extract real logic into methods or named types.
  • Treat concurrency structure and business logic as separate concerns.

Course illustration
Course illustration

All Rights Reserved.