Call to Thread run() Instead of start()

Description

Call to Thread run() Instead of start() is a programming error in Java where developers mistakenly invoke the run() method directly on a Thread object instead of calling start(). When run() is called directly, the code executes synchronously in the calling thread rather than spawning a new thread. This defeats the purpose of multithreading entirely—the method executes sequentially, blocking the caller until completion. The intended concurrent execution never occurs, which can cause performance issues, deadlocks, or incorrect behavior in applications that depend on parallel execution.

Risk

Calling run() instead of start() eliminates expected concurrency, causing serious application issues. Operations designed to run in parallel execute sequentially, creating performance bottlenecks and unresponsive applications. GUI applications may freeze because blocking operations run on the event dispatch thread. Server applications lose their ability to handle concurrent requests. More critically, code that assumes concurrent execution may deadlock when running synchronously, as threads waiting for each other's results are actually the same thread. Applications may fail to meet performance requirements or exhibit race-condition-like symptoms that disappear when the bug is fixed.

Solution

Always use Thread.start() to begin thread execution rather than calling run() directly. The start() method creates a new thread of execution and then invokes run() within that new thread context. Use static analysis tools and IDE warnings to detect direct run() calls on Thread objects. Consider using higher-level concurrency utilities like ExecutorService, which provide cleaner APIs that are less prone to this error. Code reviews should specifically check thread initialization patterns. When testing concurrent code, verify that operations actually execute in parallel using thread identification or timing analysis.

Common Consequences

ImpactDetails
OtherScope: Other

Quality Degradation - The application fails to achieve intended concurrency, resulting in sequential execution where parallel execution was designed.
AvailabilityScope: Availability

DoS: Resource Consumption - Sequential execution of intended parallel operations causes performance degradation, potentially making the application unresponsive.

Example Code

Vulnerable Code

// Vulnerable: Calling run() directly instead of start()
public class VulnerableThreadExample {

    public void processDataConcurrently(List<DataItem> items) {
        for (DataItem item : items) {
            Thread worker = new Thread(new DataProcessor(item));

            // Vulnerable: Direct run() call - executes SYNCHRONOUSLY
            worker.run();  // Wrong! This blocks and runs in current thread

            // Each item processes sequentially, not in parallel
            // Expected parallel processing never occurs
        }
    }
}

// Vulnerable: Background task that blocks
public class VulnerableBackgroundTask {

    public void startBackgroundProcess() {
        Thread backgroundThread = new Thread(() -> {
            // Long-running operation
            performExpensiveCalculation();
            updateDatabase();
            sendNotifications();
        });

        // Vulnerable: Caller blocks until all operations complete
        backgroundThread.run();  // Wrong!

        // This line is reached only after background task completes
        System.out.println("Background task started");  // Misleading message
    }
}

// Vulnerable: GUI freezing due to run() call
public class VulnerableGuiApplication extends JFrame {

    private void loadDataButton_Click() {
        Thread loaderThread = new Thread(() -> {
            // Fetch data from remote server (slow operation)
            List<Record> records = fetchFromServer();
            updateTable(records);
        });

        // Vulnerable: GUI thread blocks, application freezes
        loaderThread.run();  // Wrong! Freezes UI until data loads

        // User sees frozen, unresponsive application
    }
}

// Vulnerable: Deadlock due to synchronous execution
public class VulnerableDeadlockExample {
    private final Object lock = new Object();
    private String result = null;

    public String fetchWithTimeout() {
        Thread fetchThread = new Thread(() -> {
            synchronized (lock) {
                result = performFetch();
                lock.notifyAll();
            }
        });

        // Vulnerable: This creates a deadlock!
        synchronized (lock) {
            fetchThread.run();  // Wrong! Runs in CURRENT thread
            // run() tries to acquire lock that this thread already holds
            // With start(), different thread would wait for lock

            try {
                lock.wait(5000);  // Wait for result
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }

        return result;
    }
}

// Vulnerable: Thread pool simulation fails
public class VulnerableWorkerPool {
    private List<Thread> workers = new ArrayList<>();

    public void submitTasks(List<Runnable> tasks) {
        for (Runnable task : tasks) {
            Thread worker = new Thread(task);
            workers.add(worker);
        }

        // Vulnerable: Tasks run sequentially, not concurrently
        for (Thread worker : workers) {
            worker.run();  // Wrong! Each task blocks until complete
        }

        // Performance is no better than single-threaded execution
    }
}
// Vulnerable: Subclass calling super.run()
public class VulnerableCustomThread extends Thread {

    @Override
    public void run() {
        System.out.println("Custom processing");
        // ... do work
    }

    public void execute() {
        // Vulnerable: Should call start(), not run()
        this.run();  // Wrong! Synchronous execution
    }
}

// Vulnerable: Anonymous inner class
public class VulnerableAnonymousThread {

    public void process() {
        // Vulnerable: Direct run() on anonymous Thread
        new Thread() {
            @Override
            public void run() {
                expensiveOperation();
            }
        }.run();  // Wrong! Should be .start()
    }
}

Fixed Code

// Fixed: Using start() for proper concurrent execution
public class SecureThreadExample {

    public void processDataConcurrently(List<DataItem> items) {
        List<Thread> threads = new ArrayList<>();

        for (DataItem item : items) {
            Thread worker = new Thread(new DataProcessor(item));
            threads.add(worker);

            // Fixed: start() creates new thread and calls run() in it
            worker.start();  // Correct! Runs concurrently
        }

        // Wait for all threads to complete
        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

// Fixed: Non-blocking background task
public class SecureBackgroundTask {

    public void startBackgroundProcess() {
        Thread backgroundThread = new Thread(() -> {
            performExpensiveCalculation();
            updateDatabase();
            sendNotifications();
        });

        // Fixed: Caller continues immediately
        backgroundThread.start();  // Correct!

        // This executes immediately while background task runs
        System.out.println("Background task started");
    }
}

// Fixed: Responsive GUI with proper threading
public class SecureGuiApplication extends JFrame {

    private void loadDataButton_Click() {
        // Disable button to prevent double-clicks
        loadButton.setEnabled(false);

        Thread loaderThread = new Thread(() -> {
            List<Record> records = fetchFromServer();

            // Update GUI on event dispatch thread
            SwingUtilities.invokeLater(() -> {
                updateTable(records);
                loadButton.setEnabled(true);
            });
        });

        // Fixed: GUI remains responsive
        loaderThread.start();  // Correct! UI thread continues
    }
}

// Fixed: No deadlock with proper thread creation
public class SecureNoDeadlockExample {
    private final Object lock = new Object();
    private volatile String result = null;

    public String fetchWithTimeout() {
        Thread fetchThread = new Thread(() -> {
            String fetchedResult = performFetch();
            synchronized (lock) {
                result = fetchedResult;
                lock.notifyAll();
            }
        });

        synchronized (lock) {
            // Fixed: start() creates separate thread that can acquire lock later
            fetchThread.start();  // Correct!

            try {
                lock.wait(5000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }

        return result;
    }
}
// Better: Use ExecutorService for thread management
import java.util.concurrent.*;

public class ModernConcurrencyExample {

    private final ExecutorService executor = Executors.newFixedThreadPool(4);

    public void processDataConcurrently(List<DataItem> items) {
        List<Future<?>> futures = new ArrayList<>();

        for (DataItem item : items) {
            // Better: ExecutorService handles thread lifecycle
            Future<?> future = executor.submit(() -> {
                new DataProcessor(item).process();
            });
            futures.add(future);
        }

        // Wait for completion
        for (Future<?> future : futures) {
            try {
                future.get();
            } catch (InterruptedException | ExecutionException e) {
                handleError(e);
            }
        }
    }

    public void shutdown() {
        executor.shutdown();
    }
}

// Better: CompletableFuture for async operations
public class AsyncExample {

    public CompletableFuture<List<Record>> loadDataAsync() {
        return CompletableFuture.supplyAsync(() -> {
            return fetchFromServer();
        });
    }

    public void example() {
        loadDataAsync()
            .thenAccept(records -> {
                // Process on completion
                updateTable(records);
            })
            .exceptionally(ex -> {
                // Handle errors
                showError(ex);
                return null;
            });

        // Caller continues immediately
        System.out.println("Loading started...");
    }
}

// Fixed: Custom thread class with proper execution
public class SecureCustomThread extends Thread {

    @Override
    public void run() {
        System.out.println("Custom processing in thread: " +
            Thread.currentThread().getName());
    }

    public void execute() {
        // Fixed: Call start() for concurrent execution
        this.start();  // Correct!
    }

    // Better: Prevent accidental run() calls
    public static void executeTask(Runnable task) {
        Thread thread = new Thread(task);
        thread.start();  // Always use start()
    }
}

// Parallel streams as alternative
public class ParallelStreamExample {

    public void processDataParallel(List<DataItem> items) {
        // Alternative: Use parallel streams for simple parallel operations
        items.parallelStream()
            .forEach(item -> new DataProcessor(item).process());
    }
}

CVE Examples

No specific CVEs are commonly attributed to this CWE. However, the bug pattern is well-documented in Java programming resources and static analysis tools.


References

  1. MITRE Corporation. "CWE-572: Call to Thread run() Instead of start()." https://cwe.mitre.org/data/definitions/572.html
  2. Oracle. "Java Thread Documentation."
  3. FindBugs. "RU: Invocation of run on a Thread (RU_INVOKE_RUN)."