← Java and object-oriented programming for interviews

Lesson 4 of 4

Multithreading, and a first look at Spring Boot

2 min read

Threads

java
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.

java
private int count = 0;
public synchronized void increment() { count++; }   // one thread at a time
// or
private final AtomicInteger count = new AtomicInteger();
count.incrementAndGet();
  • synchronized gives mutual exclusion and visibility.
  • volatile gives 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) vs sleep() (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 new their collaborators. That makes code testable.
  • Auto-configuration: add a starter (spring-boot-starter-web) and sensible defaults are configured.
java
@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?
  • volatile vs synchronized?
  • What does dependency injection give you?

Sign up free to save your progress.