← Weride Interview Insights

Weride·Software Engineer·Technical Phone Screen·Intermediate

Intermediate
May 2026

Summary

Interviewed for a Software Engineer role at WeRide with a heavy focus on Java concurrency. The questions drilled pretty deep into the memory model and thread-safety primitives, so if you're not solid on that stuff going in, you'll feel it.

Questions Asked (6)

Q1

What are the differences between Thread, Runnable, and Callable in Java? When would you use each?

Technical Trade-offs
Author's notes

Ran through the basics fine but stumbled a bit when they pushed on Callable vs Runnable beyond just 'Callable returns a value.' Should've talked more concretely about Future and how you actually get the result back.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by defining each interface and highlighting their key differences in terms of return values, exception handling, and task execution. Then, explain when to use each based on the need for a result, exception propagation, and compatibility with ExecutorService. Finally, provide a practical example or scenario to illustrate your reasoning.

Pro tip: Mention that Callable is often used with ExecutorService and Future to obtain results asynchronously, and that Runnable can be wrapped into a Callable using Executors.callable() if needed. This shows depth and practical knowledge.

1. Define the interfaces

Briefly describe Thread, Runnable, and Callable, noting that Thread is a class, while Runnable and Callable are functional interfaces.

2. Compare key differences

Highlight that Runnable's run() returns void and cannot throw checked exceptions, while Callable's call() returns a value and can throw checked exceptions.

3. Explain usage scenarios

Discuss when to use each: Thread for simple cases, Runnable for tasks without results, and Callable for tasks that return results or throw exceptions, especially with ExecutorService.

4. Discuss integration with concurrency utilities

Mention that Callable is designed for use with ExecutorService and Future, while Runnable can be used with Thread or ExecutorService.

5. Summarize with trade-offs

Conclude by emphasizing that the choice depends on whether you need a result, exception handling, and the level of abstraction you prefer.

Key Points to Mention

  • Thread is a class, Runnable and Callable are interfaces.
  • Runnable.run() returns void and cannot throw checked exceptions; Callable.call() returns a value and can throw checked exceptions.
  • Callable is typically used with ExecutorService and Future to obtain results asynchronously.
  • Runnable can be executed by a Thread or an ExecutorService, but does not return a result.
  • Callable can be converted to Runnable using Executors.callable() if needed.
  • Use Callable when you need to return a result or handle checked exceptions; use Runnable for fire-and-forget tasks.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q2

Can you explain the Java Memory Model and how it governs visibility and ordering between threads?

Technical Trade-offsSystem Design
Author's notes

This is where I felt the most pressure.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by defining the Java Memory Model (JMM) as a specification that defines how threads interact through memory, then explain the core concepts of visibility and ordering with concrete examples like volatile, synchronized, and happens-before. Finally, connect these concepts to practical implications for writing correct concurrent code, such as avoiding data races and ensuring safe publication.

Pro tip: Mention that the JMM is not about physical memory but about the rules that allow compilers and CPUs to reorder instructions, and that understanding happens-before is key to reasoning about concurrency without relying on implementation details.

1. Define the JMM and its purpose

Explain that the JMM is a specification that defines the semantics of multi-threaded programs in Java, ensuring predictable behavior across different hardware and JVM implementations.

2. Explain visibility and ordering

Describe visibility as when one thread's writes become visible to other threads, and ordering as the sequence in which memory operations appear to execute. Mention that without synchronization, threads may see stale or reordered values.

3. Introduce happens-before and synchronization

Introduce the happens-before relationship as the core rule that guarantees visibility and ordering. Give examples: volatile writes, synchronized blocks, thread start/join, and final fields.

4. Discuss practical implications and trade-offs

Explain how to use these tools correctly (e.g., volatile for flags, synchronized for compound actions) and the performance trade-offs between them. Mention common pitfalls like double-checked locking without volatile.

Key Points to Mention

  • Happens-before relationship and its role in guaranteeing visibility and ordering
  • Volatile variables: visibility and ordering but not atomicity
  • Synchronized blocks: mutual exclusion and memory visibility
  • Final fields: safe publication and initialization safety
  • Data races and the importance of avoiding them
  • The difference between sequential consistency and the JMM's relaxed ordering

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q3

What are the differences between volatile, synchronized, Lock, and the Atomic classes? When is each appropriate?

Technical Trade-offs
Author's notes

Talked through volatile for visibility-only scenarios, synchronized for mutual exclusion, and Atomic for lock-free single-variable updates.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by categorizing these concurrency mechanisms by their level of abstraction and guarantees (visibility, atomicity, ordering). Then compare them on performance, scalability, and use cases, emphasizing when each is appropriate based on contention and complexity.

Pro tip: Mention that volatile is often misunderstood as a replacement for synchronization; clarify that it only guarantees visibility and ordering, not atomicity. Also, highlight that Atomic classes use CAS (compare-and-swap) and are lock-free, which can outperform locks under low to moderate contention.

1. Define each mechanism

Briefly explain what volatile, synchronized, Lock, and Atomic classes are and their primary purpose in concurrent programming.

2. Compare guarantees

Discuss the guarantees each provides: visibility, atomicity, ordering, and mutual exclusion. Note that volatile provides visibility and ordering but not atomicity; synchronized and Lock provide all; Atomic classes provide atomicity for single variables.

3. Analyze performance and scalability

Compare performance characteristics: volatile is lightweight but limited; synchronized is simple but can be less scalable; Lock offers more flexibility (e.g., tryLock, fairness); Atomic classes are lock-free and often faster under low contention.

4. Discuss appropriate use cases

Explain when to use each: volatile for simple flags or status indicators; synchronized for simple mutual exclusion; Lock for advanced locking scenarios; Atomic classes for counters or single-variable atomic updates.

5. Summarize trade-offs

Conclude with a summary of trade-offs: simplicity vs. control, performance vs. safety, and how to choose based on contention and complexity.

Key Points to Mention

  • volatile ensures visibility and prevents instruction reordering but does not guarantee atomicity for compound operations.
  • synchronized provides mutual exclusion and memory visibility, but can lead to blocking and reduced scalability.
  • Lock (e.g., ReentrantLock) offers extended capabilities like tryLock, lockInterruptibly, and fairness policies.
  • Atomic classes (e.g., AtomicInteger) use CAS operations for lock-free thread-safe updates on single variables.
  • Performance: volatile is cheapest, Atomic classes are efficient under low contention, locks are heavier but necessary for complex atomicity.
  • Use cases: volatile for flags, synchronized for simple critical sections, Lock for advanced control, Atomic for counters and single-variable updates.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q4

Why is i++ not thread-safe, and how does this relate to the lost update problem?

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

Pretty standard once you frame it as a read-modify-write sequence.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by explaining that i++ is a compound operation consisting of read, modify, and write steps, which are not atomic. Then, describe how concurrent execution can interleave these steps, leading to lost updates. Finally, discuss solutions like atomic variables or synchronization.

Pro tip: Mention that even though i++ looks like a single operation, it compiles to multiple machine instructions, and modern CPUs may reorder them; this shows depth. Also, relate it to real-world scenarios like counters in web applications to demonstrate practical awareness.

1. Define i++ operation

Explain that i++ is shorthand for i = i + 1, which involves reading the current value, incrementing it, and writing it back.

2. Explain non-atomicity

Highlight that these three steps are not atomic; they can be interleaved when multiple threads execute concurrently.

3. Illustrate lost update

Provide a concrete example: two threads read the same value, both increment, and write back, resulting in one increment being lost.

4. Connect to lost update problem

Define the lost update problem as a classic concurrency issue where updates are overwritten, and explain that i++ is a prime example.

5. Discuss solutions

Mention synchronization mechanisms like locks, atomic variables (e.g., AtomicInteger in Java), or compare-and-swap to ensure thread safety.

Key Points to Mention

  • i++ is not atomic; it consists of read-modify-write operations.
  • Concurrent threads can interleave these operations, causing lost updates.
  • The lost update problem occurs when two transactions read the same value and update it, with one overwriting the other.
  • Race conditions arise due to lack of synchronization.
  • Solutions include using synchronized blocks, atomic classes, or locks.
  • Memory visibility issues may also arise without proper synchronization (e.g., stale values).

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q5

How would you implement a thread-safe counter without using synchronized?

Technical Trade-offs
Author's notes

Went with AtomicInteger and compareAndSet.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying that 'without synchronized' means avoiding intrinsic locks, then present lock-free alternatives like AtomicInteger or LongAdder, explaining their internal CAS mechanisms. Compare trade-offs between these options in terms of contention, scalability, and use cases, and mention other approaches like ReentrantLock or StampedLock if appropriate.

Pro tip: Demonstrate awareness of contention and scalability: for high-concurrency scenarios, LongAdder often outperforms AtomicInteger due to reduced contention, but AtomicInteger provides stronger consistency guarantees. Mentioning this shows you understand real-world performance implications.

1. Clarify the constraint

Confirm that 'without synchronized' means avoiding the synchronized keyword and intrinsic locks, but other synchronization primitives like locks or atomics are allowed.

2. Present atomic variable solutions

Describe using java.util.concurrent.atomic classes such as AtomicInteger or AtomicLong, which use CAS (compare-and-swap) operations for lock-free thread safety.

3. Discuss advanced alternatives

Mention LongAdder for high-contention scenarios, explaining how it reduces contention by maintaining multiple cells, and note that it trades off strong consistency for performance.

4. Compare trade-offs

Analyze the trade-offs: AtomicInteger offers strong consistency but may suffer under high contention; LongAdder scales better but provides only eventual consistency for reads; ReentrantLock offers more flexibility but is not lock-free.

5. Conclude with recommendation

Summarize that the choice depends on the specific requirements: for low contention, AtomicInteger is simple and sufficient; for high contention, LongAdder is preferable; and for complex operations, consider locks.

Key Points to Mention

  • CAS (Compare-and-Swap) and its role in lock-free thread safety
  • AtomicInteger and AtomicLong from java.util.concurrent.atomic
  • LongAdder and its cell-based approach for high concurrency
  • Trade-offs between consistency, contention, and scalability
  • Alternative synchronization primitives like ReentrantLock or StampedLock
  • The ABA problem and how AtomicStampedReference addresses it

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q6

What new features or improvements were introduced in a recent JDK version that you found useful?

Technical Trade-offs
Author's notes

Blanked a bit here.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Choose one or two recent JDK features you have actually used, and explain how they improved your code's performance, readability, or maintainability. Connect the benefits to real project scenarios, and briefly mention any trade-offs or migration considerations.

Pro tip: Avoid listing features you haven't used; instead, focus on one feature and dive deep into how it solved a specific problem, showing you evaluate technology based on practical impact.

1. Select a relevant JDK version and feature

Pick a recent JDK release (e.g., JDK 17, 21) and a feature you have hands-on experience with, such as records, pattern matching, or virtual threads.

2. Describe the feature and its purpose

Briefly explain what the feature does and the problem it addresses, keeping it concise for the interviewer.

3. Share a concrete use case

Illustrate how you applied this feature in a real project, including the specific benefits you observed (e.g., reduced boilerplate, improved performance).

4. Discuss trade-offs or limitations

Mention any drawbacks, such as learning curve, compatibility issues, or performance overhead, to show balanced thinking.

5. Relate to the role or company

Connect the feature's benefits to the kind of work done at Weride, such as high-performance computing or scalable systems, to show alignment.

Key Points to Mention

  • Records (JDK 16) for concise data carriers
  • Pattern matching for instanceof (JDK 16) and switch (JDK 21) for cleaner conditionals
  • Virtual threads (JDK 21) for scalable concurrency
  • Sealed classes (JDK 17) for controlled inheritance
  • Text blocks (JDK 15) for readable multi-line strings
  • Performance improvements in garbage collectors (e.g., ZGC, Shenandoah)

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.