Lesson 4 of 4
Multithreading, and a first look at Spring Boot
2 min read
Threads
Runnable task = () -> System.out.println("Working in " + Thread.currentThread().getName());
new Thread(task).start(); // start() creates a new thread; run() would just call it on this one
ExecutorService pool = Executors.newFixedThreadPool(4); // prefer pools to raw threads
Future<Integer> f = pool.submit(() -> 42);
f.get();
pool.shutdown();Thread states: NEW → RUNNABLE → (BLOCKED / WAITING / TIMED_WAITING) → TERMINATED.
Race conditions and synchronization
count++ is read-modify-write: two threads can interleave and lose an update.
private int count = 0;
public synchronized void increment() { count++; } // one thread at a time
// or
private final AtomicInteger count = new AtomicInteger();
count.incrementAndGet();synchronizedgives mutual exclusion and visibility.volatilegives visibility only (every thread sees the latest value) but not atomicity.- Deadlock: two threads each hold a lock the other needs. Prevent by always acquiring locks in the same order, using timeouts (
tryLock), or avoiding nested locks. wait()/notify()(on an object's monitor) vssleep()(pauses without releasing locks).- Higher-level tools:
ConcurrentHashMap,CountDownLatch,CompletableFuture, and virtual threads (Java 21) for huge numbers of blocking tasks.
Spring Boot in one lesson
Spring Boot is the most common Java backend framework in Indian companies.
- Inversion of control / dependency injection: the framework creates objects ("beans") and passes dependencies in, so classes don't
newtheir collaborators. That makes code testable. - Auto-configuration: add a starter (
spring-boot-starter-web) and sensible defaults are configured.
@RestController
@RequestMapping("/api/jobs")
public class JobController {
private final JobService service;
public JobController(JobService service) { this.service = service; } // constructor injection
@GetMapping("/{id}")
public JobDto get(@PathVariable long id) { return service.find(id); }
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public JobDto create(@Valid @RequestBody CreateJob req) { return service.create(req); }
}
@Entity
public class Job { @Id @GeneratedValue Long id; String title; int salary; }
public interface JobRepository extends JpaRepository<Job, Long> {
List<Job> findByTitleContainingIgnoreCase(String q); // query derived from the method name
}Layers: Controller → Service (business logic, @Transactional) → Repository (Spring Data JPA) → database. Know @Component, @Service, @Repository, @Autowired, @Transactional, bean scopes (singleton by default) and the N+1 problem with lazy-loaded JPA relations.
Check yourself
- Why is
count++not thread-safe? volatilevssynchronized?- What does dependency injection give you?
Sign up free to save your progress.