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
| Impact | Details |
|---|---|
| Other | Scope: Other Quality Degradation - The application fails to achieve intended concurrency, resulting in sequential execution where parallel execution was designed. |
| Availability | Scope: 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
- MITRE Corporation. "CWE-572: Call to Thread run() Instead of start()." https://cwe.mitre.org/data/definitions/572.html
- Oracle. "Java Thread Documentation."
- FindBugs. "RU: Invocation of run on a Thread (RU_INVOKE_RUN)."