Advanced Java ProgrammingUnit 112 min read
Advanced Java Concepts & Multithreading: Threads, Paths, and Anonymous Classes
Unit 1 of Advanced Java Programming covers core advanced concepts (path vs. classpath, anonymous inner classes) and deep dives into multithreading—lifecycle, synchronization, thread creation methods, and real-world applications like concurrent file I/O. Includes architecture diagrams, code examples, and exam-focused co
TAKEAWAYS:
- Path vs. Classpath: Understand the distinction between system paths (executable locations) and classpath (JVM’s search path for
.classfiles) to resolveClassNotFoundException. - Anonymous Inner Classes: Use them for one-time implementations (e.g.,
Runnablefor threads) to avoid verbose subclassing, but avoid overuse due to readability trade-offs. - Multithreading Basics: Master thread lifecycle (NEW → RUNNABLE → BLOCKED → TERMINATED) and creation methods (
extends Thread,implements Runnable,ExecutorService). - Synchronization: Use
synchronizedblocks/methods to prevent race conditions in shared resources (e.g., file I/O, static variables). - Thread Priorities: Leverage
setPriority()(1–10) for cooperative scheduling, though OS scheduling may override it. - Exam Pitfalls: Avoid mixing
ThreadandRunnablein the same program; preferExecutorServicefor modern thread pools.
1. Path vs. Classpath in Java
Definitions
Path: Environment variable listing directories where executable files (e.g.,
java,javac) reside.echo $PATH # Linux/Mac echo %PATH% # WindowsExample output:
/usr/bin:/usr/local/bin:/opt/java/binClasspath: JVM’s search path for
.classfiles or JARs. Set via-classpathorCLASSPATHenvironment variable.java -classpath ".:/lib/*" com.example.Main
Key Differences
| Feature | Path | Classpath |
|---|---|---|
| Purpose | Locates executables (binaries) | Locates .class/JAR files |
| Default | System-dependent (e.g., /usr/bin) |
. (current directory) |
| Syntax | : (Unix) or ; (Windows) |
-classpath or CLASSPATH |
| Example Use | Running java command |
Compiling (javac) or running classes |
When to Use Which?
- Use classpath when:
- Compiling (
javac) or running Java programs. - Resolving dependencies across packages (e.g.,
import com.example.*).
- Compiling (
- Use path when:
- Configuring system-wide access to Java tools (
javac,jar).
- Configuring system-wide access to Java tools (
Example: Setting Classpath
// Compile with custom classpath
javac -classpath "lib/*:." MyProgram.java
// Run with classpath
java -classpath "lib/*:." MyProgram
Common Errors & Fixes
| Error | Cause | Solution |
|---|---|---|
ClassNotFoundException |
Classpath missing .class file |
Add directory to classpath: -cp ./bin |
NoClassDefFoundError |
Class exists but not at runtime | Rebuild project or check CLASSPATH |
Could not find or load main |
Wrong classpath order | Ensure . (current dir) is first |
2. Anonymous Inner Classes
Definition
An anonymous inner class is a class declared without a name, typically for one-time use. It must extend a superclass or implement an interface.
Syntax
new SuperClassOrInterface() {
// Override methods or add new ones
};
Use Cases
- Thread Creation:
Thread t = new Thread(new Runnable() { @Override public void run() { System.out.println("Thread running!"); } }); - Event Handlers (e.g., Swing
ActionListener):JButton button = new JButton("Click"); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println("Button clicked!"); } }); - Comparators:
List<String> list = new ArrayList<>(); Collections.sort(list, new Comparator<String>() { public int compare(String s1, String s2) { return s1.length() - s2.length(); } });
Advantages
- Conciseness: Avoids verbose subclass declarations.
- Flexibility: Can override methods or add fields/methods inline.
- Local Scope: Accesses enclosing class variables (if non-final).
Disadvantages
- Readability: Overuse can make code harder to debug.
- Performance: Slight overhead due to anonymous class instantiation.
- Limited Reusability: Not suitable for complex logic reused across methods.
When to Prefer Anonymous Classes?
| Scenario | Anonymous Class | Traditional Class |
|---|---|---|
| One-time implementation (e.g., thread) | ✅ Best | ❌ Overkill |
| Complex logic reused in multiple places | ❌ Avoid | ✅ Better |
| Lambda expressions available (Java 8+) | ❌ Replace with lambda | ✅ Legacy support |
Example: Anonymous Class vs. Lambda
// Anonymous class (Java 7)
Thread t1 = new Thread(new Runnable() {
public void run() { System.out.println("Anonymous"); }
});
// Lambda (Java 8+)
Thread t2 = new Thread(() -> System.out.println("Lambda"));
3. Multithreading in Java
Definition
Multithreading allows concurrent execution of two or more parts of a program to maximize CPU utilization. Java supports threads via:
Threadclass (extendsjava.lang.Thread).Runnableinterface (implementsrun()).ExecutorService(thread pools, Java 5+).
Why Multithreading?
- Performance: Utilizes multi-core CPUs efficiently.
- Responsiveness: Keeps GUI/apps responsive (e.g., background tasks).
- Resource Sharing: Multiple threads can access shared data (with synchronization).
Thread Lifecycle
stateDiagram-v2
[*] --> NEW: Thread created (new Thread())
NEW --> RUNNABLE: start() called
RUNNABLE --> RUNNING: OS scheduler picks thread
RUNNING --> BLOCKED: wait(), sleep(), I/O
RUNNING --> WAITING: wait(), join()
RUNNING --> TIMED_WAITING: sleep(time), wait(time)
BLOCKED --> RUNNABLE: Condition met (e.g., I/O complete)
WAITING --> RUNNABLE: notify()/notifyAll()
TIMED_WAITING --> RUNNABLE: Timeout reached
RUNNING --> TERMINATED: run() completes
TERMINATED --> [*]Thread Creation Methods
| Method | Example | Pros | Cons |
|---|---|---|---|
Extend Thread class |
class MyThread extends Thread { ... } |
Simple for single-thread tasks | Single inheritance limitation |
Implement Runnable |
new Thread(new MyRunnable()).start() |
Flexible (can extend another class) | Extra boilerplate (Thread wrapper) |
ExecutorService (Thread Pool) |
ExecutorService es = Executors.newFixedThreadPool(3); |
Reuses threads, manages lifecycle | Complexity for simple tasks |
Example: Thread Creation
// Method 1: Extend Thread
class MyThread extends Thread {
public void run() {
System.out.println("Thread running: " + Thread.currentThread().getName());
}
}
MyThread t1 = new MyThread();
t1.start();
// Method 2: Implement Runnable
Runnable task = () -> System.out.println("Runnable thread: " + Thread.currentThread().getName());
Thread t2 = new Thread(task);
t2.start();
// Method 3: ExecutorService
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> System.out.println("Executor thread: " + Thread.currentThread().getName()));
executor.shutdown();
Thread Priorities
Java threads have priorities (1–10, where 1 = lowest, 10 = highest). Default is 5.
Thread t = new Thread(() -> System.out.println("High priority"));
t.setPriority(Thread.MAX_PRIORITY); // 10
t.start();
- Note: Priority is hint-only; OS scheduler may ignore it.
Synchronization
Prevents race conditions when multiple threads access shared data.
Synchronized Methods
class Counter {
private int count = 0;
public synchronized void increment() { // Only one thread can execute this at a time
count++;
}
}
Synchronized Blocks
class Counter {
private int count = 0;
public void increment() {
synchronized(this) { // Lock on 'this' object
count++;
}
}
}
Deadlock Example
Object lock1 = new Object();
Object lock2 = new Object();
new Thread(() -> {
synchronized(lock1) {
synchronized(lock2) { System.out.println("Thread 1"); }
}
}).start();
new Thread(() -> {
synchronized(lock2) {
synchronized(lock1) { System.out.println("Thread 2"); }
}
}).start();
// Both threads wait indefinitely → Deadlock!
Inter-Thread Communication
Use wait(), notify(), and notifyAll() on shared objects.
class SharedData {
private int data;
private boolean ready = false;
public synchronized void produce(int value) {
while (ready) wait(); // Wait if consumer is ready
data = value;
ready = true;
notify(); // Notify consumer
}
public synchronized int consume() {
while (!ready) wait(); // Wait if producer hasn’t set data
ready = false;
notify(); // Notify producer
return data;
}
}
4. Practical Example: Concurrent File I/O
Task: Read employee data from keyboard and write to emp.doc using multithreading.
Solution
import java.io.*;
import java.util.Scanner;
class EmployeeWriter implements Runnable {
private BufferedWriter writer;
private String data;
public EmployeeWriter(String filename, String data) throws IOException {
this.writer = new BufferedWriter(new FileWriter(filename));
this.data = data;
}
@Override
public void run() {
try {
writer.write(data);
writer.newLine();
writer.close();
System.out.println("Data written to file.");
} catch (IOException e) {
System.err.println("Error writing to file: " + e.getMessage());
}
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter employee name: ");
String name = scanner.nextLine();
// Thread for writing to file
Thread writerThread = new Thread(new EmployeeWriter("emp.doc", name));
writerThread.start();
// Main thread continues
System.out.println("Entering data...");
scanner.close();
}
}
Exception Handling
try {
// Risky operations (e.g., file I/O, network)
} catch (FileNotFoundException e) {
System.err.println("File not found!");
} catch (IOException e) {
System.err.println("I/O error: " + e.getMessage());
} finally {
if (writer != null) writer.close(); // Ensure resources are released
}
5. Exam Tip: Common Pitfalls & Focus Areas
Do’s
- Thread Lifecycle: Draw the state diagram and explain transitions (e.g.,
NEW→RUNNABLEviastart()). - Synchronization: Always use
synchronizedfor shared resources. Mentionwait()/notify()for inter-thread communication. - Anonymous Classes: Compare with lambdas (Java 8+) and traditional classes. Highlight use cases (e.g.,
Runnablefor threads). - Classpath: Differentiate from
PATHand explain-classpathvs.CLASSPATHenvironment variable.
Don’ts
- Mix
ThreadandRunnable: Stick to one approach per program. - Ignore
finally: Always close resources (e.g.,FileWriter) infinallyblocks. - Overuse Threads: Prefer
ExecutorServicefor thread pools to avoid resource exhaustion. - Assume Priority Works: State that thread priority is a hint, not a guarantee.
High-Score Tips
- Code + Explanation: Always pair code snippets with step-by-step explanations (e.g., "Here,
synchronized(this)ensures only one thread can incrementcountat a time"). - Diagrams: Draw the thread lifecycle diagram or classpath hierarchy.
- Error Handling: Show
try-catch-finallyblocks for file/network operations. - Real-World Analogy: Relate multithreading to scenarios like:
- A call center (threads = agents,
ExecutorService= call queue). - Bank transactions (synchronization = locking accounts during transfers).
- A call center (threads = agents,
Past Exam Patterns
- Short Questions (5 marks):
- Differentiate
pathvs.classpath. - When to use anonymous inner classes (e.g., "for one-time
Runnableimplementations").
- Differentiate
- Programming (10–15 marks):
- Write a multithreaded program (e.g., read from keyboard, write to file with synchronization).
- Implement
RunnableorCallablewithExecutorService.
- Theory (10 marks):
- Explain thread lifecycle with a diagram.
- Describe synchronization and deadlocks with examples.
stateDiagram-v2
[*] --> NEW: Thread created
NEW --> RUNNABLE: start()
RUNNABLE --> RUNNING: OS scheduler
RUNNING --> BLOCKED: wait()/sleep()
RUNNING --> WAITING: wait()
RUNNING --> TIMED_WAITING: sleep(time)
BLOCKED --> RUNNABLE: Condition met
WAITING --> RUNNABLE: notify()
TIMED_WAITING --> RUNNABLE: Timeout
RUNNING --> TERMINATED: run() completes
TERMINATED --> [*]Based on the TU BSc CSIT syllabus for Advanced Java Programming (CSC409), unit 1.
Discussion
Loading…