← Back to Home

Code-Alongs for Unit 7 Sprint 26

Multi-Threading Code-Along

Practice creating and managing multiple threads in Java applications. You'll learn how to initialize threads, run tasks concurrently, and understand thread lifecycle.

View on GitHub

Creating Threads

Learn how to create threads using both the Thread class directly and by implementing the Runnable interface.

// Creating a thread by extending Thread class
public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread running: " + getName());
    }
}

// Usage
MyThread thread = new MyThread();
thread.start();

// Creating a thread using Runnable
Runnable task = () -> {
    System.out.println("Task running in thread: " + Thread.currentThread().getName());
};

Thread thread2 = new Thread(task);
thread2.start();

Thread Safety Code-Along

Learn techniques to ensure thread safety in your concurrent applications. This code-along focuses on synchronization mechanisms and preventing race conditions.

View on GitHub

Thread Safety Mechanisms

Explore different techniques to make your code thread-safe, including synchronization, locks, and atomic variables.

// Using synchronized methods
public class BankAccount {
    private double balance;
    
    public synchronized void deposit(double amount) {
        balance += amount;
    }
    
    public synchronized void withdraw(double amount) {
        if (balance >= amount) {
            balance -= amount;
        }
    }
    
    public synchronized double getBalance() {
        return balance;
    }
}

// Using synchronized blocks for finer control
public class BankAccountWithLock {
    private double balance;
    private final Object lock = new Object();
    
    public void deposit(double amount) {
        synchronized(lock) {
            balance += amount;
        }
    }
    
    public boolean withdraw(double amount) {
        synchronized(lock) {
            if (balance >= amount) {
                balance -= amount;
                return true;
            }
            return false;
        }
    }
}

Executor Service Weather Reports

Build a weather reporting system using Java's Executor Service. Learn to manage thread pools, submit tasks, and handle responses efficiently.

View on GitHub

Working with ExecutorService

Learn how to use ExecutorService to manage thread pools for efficient asynchronous task execution.

// Setting up an ExecutorService
ExecutorService executor = Executors.newFixedThreadPool(4);

// Creating a weather service task
class WeatherService implements Callable<WeatherReport> {
    private final String location;
    
    public WeatherService(String location) {
        this.location = location;
    }
    
    @Override
    public WeatherReport call() throws Exception {
        // Simulate API call to get weather data
        Thread.sleep(1000); // Simulating network delay
        return new WeatherReport(location, getRandomTemperature(), getRandomCondition());
    }
    
    private double getRandomTemperature() {
        return 60 + Math.random() * 40; // Random temp between 60-100F
    }
    
    private String getRandomCondition() {
        String[] conditions = {"Sunny", "Cloudy", "Rainy", "Windy"};
        return conditions[(int)(Math.random() * conditions.length)];
    }
}

// Submitting tasks and handling results
List<String> locations = Arrays.asList("New York", "Los Angeles", "Chicago", "Miami");
List<Future<WeatherReport>> futures = new ArrayList<>();

// Submit all tasks
for (String location : locations) {
    Future<WeatherReport> future = executor.submit(new WeatherService(location));
    futures.add(future);
}

// Process results as they complete
for (Future<WeatherReport> future : futures) {
    try {
        WeatherReport report = future.get();
        System.out.println(report);
    } catch (Exception e) {
        System.err.println("Error getting weather: " + e.getMessage());
    }
}

// Shutdown executor
executor.shutdown();

Future Tasks Code-Along

Explore asynchronous programming using Future objects in Java. Learn how to retrieve results from concurrent tasks and handle exceptions properly.

View on GitHub

Managing Future Results

Discover advanced techniques for working with Future objects to handle asynchronous computation results.

// Creating a service that returns Futures
public class DataProcessingService {
    private final ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    
    public Future<Integer> processDataAsync(List<Integer> data) {
        return executor.submit(() -> {
            // Simulate intensive processing
            Thread.sleep(500);
            return data.stream().mapToInt(Integer::intValue).sum();
        });
    }
    
    public void shutdown() {
        executor.shutdown();
    }
}

// Using the service with timeout and error handling
DataProcessingService service = new DataProcessingService();
List<List<Integer>> dataBatches = getDataBatches(); // Method to get data
List<Future<Integer>> results = new ArrayList<>();

// Submit all processing tasks
for (List<Integer> batch : dataBatches) {
    results.add(service.processDataAsync(batch));
}

// Collect results with timeout
List<Integer> processedResults = new ArrayList<>();
for (Future<Integer> result : results) {
    try {
        // Wait up to 2 seconds for each result
        Integer value = result.get(2, TimeUnit.SECONDS);
        processedResults.add(value);
    } catch (TimeoutException e) {
        System.out.println("Processing took too long, skipping batch");
        result.cancel(true); // Attempt to cancel the task
    } catch (Exception e) {
        System.err.println("Error processing batch: " + e.getMessage());
    }
}

// Clean shutdown
service.shutdown();