Running on Java 24-ea+24-2960 (Preview)
Home of The JavaSpecialists' Newsletter

Concurrency Specialist Course

"Java Concurrency in Practice" in a four-day intensive course.

Using Java 11

Duration of 4 Days


Java was built to be able to do many things at once. In computer lingo, we call this "concurrency". This is the main reason why Java is so useful.

Java programmers need to master concurrency. If they do not, system might break on the busiest shopping day. Concurrency bugs tend to appear when we do a lot of things at the same time, such as dealing with many clients.

During this 4-day course, you will learn how to write safe multi-threaded Java code that performs well on your hardware. You will learn techniques to ensure visibility of your fields, to build thread safety without explicit locking. You will learn the new fork/join constructs and explore ways to parallelize your algorithms.

We also cover virtual threads, which were introduced in Java 19-preview with JEP 425, allowing us to create millions of threads in a single JVM.

"I am extremely pleased that Dr. Heinz Kabutz has created a training course based on Java Concurrency in Practice. Everyone in the Java community appreciates Dr. Heinz' work; his deep understanding of Java -- and his passion for it -- show through in everything he does. This course is sure to be a winner."
Brian Goetz, Author of Java Concurrency in Practice

Is this course for you?

If you answer "yes!" to any of the following questions, then this course is for you:

  • Has your system ever caused some strange behaviors that you could not explain? This often happens at the worst time, such as when your system is very busy. Imagine losing your biggest shopping day!
  • Have you ever wondered how some of the more advanced Java constructs work, such as the ConcurrentHashMap or ConcurrentLinkedQueue?
  • Would you like to find out how ReadWriteLocks can cause serious starvation?
  • Have you ever programmed a web application, a servlet, a JSP page, a Swing application?
  • Are you an above average Java programmer, interested to learn more?

The Java Concurrency Course is the only such training officially endorsed by Brian Goetz, and is loosely based on his best-seller book Java Concurrency in Practice, but includeing more modern features such as virtual threads, VarHandles, ForkJoin, StampedLock and ManagedBlocker. The course was written by Dr Heinz Kabutz, author of The Java Specialists' Newsletter, with animations by Victor Grazi, author of the Java Concurrent Animated Tutorial.

Java Concurrency Course Enquiry


Prerequisites

This course is ideally suited to the professional Java programmer with at least 2 years experience, who would like to learn how to truly understand Java concurrency.

  • Previous Training: java.lang.reflect.*.
  • Required Experience: At least two years of professional Java programming.
  • Preparation: Read Brian Goetz's "Java Concurrency in Practice".

Pricing Options

We have several options for you to join this course:

1. Virtual In-house Course:

Presented via video conference to your team of programmers by the author of the course. Price is €13400 for up to 10 students, above that is an additional €1200 per student.

  • Example 1: Course with 8 students taught remotely via video conference, price is €13400. Price per student is €1675.
  • Example 2: Course with 12 students taught remotely via video conference, price is €15800. Price per student is €1316.
  • Example 3: Course with 24 students taught remotely via video conference, price is €30200. Price per student is €1258.

Please contact us if you have any questions.

2. In-person In-house Course:

Presented at your company in-person by one of our Certified JavaSpecialist Instructors. Price is €19750 for up to 10 students, above that is an additional €1975 per student, plus the travel expenses of the instructor. Note that for in-person in-house courses, we need a minimum of three consecutive training days.

  • Example 1: Course with 8 students taught on-site at your company, price is €19750. Price per student is €2468.
  • Example 2: Course with 12 students taught on-site at your company, price is €23700. Price per student is €1975.
  • Example 3: Course with 18 students taught on-site at your company, price is €35550. Price per student is €1975.

Please contact us if you have any questions.

2. Open Enrollment Classroom Course:

We occasionally offer this course as a classroom course in Chania on the Island of Crete. Price for the course is €4250 per student.

We also offer this course as an open enrollment live remote course that you can attend from anywhere. Price is €2885 per student.

Please contact us if you have would like to make a booking or if you have any questions.

4. Self-Paced Course:

This course is currently not available as a self-paced course.

Please contact us if you have any questions.

* Prices exclude EU VAT and withholding taxes where applicable. Please contact us for an exact quote for your country.

Open Courses

Concurrency courses offered:

Location Dates Presenter Language Fees

All our courses are offered as in-house courses. Please contact us on heinz@javaspecialists.eu.

* Price is excluding EU VAT where applicable. Please contact us for an exact quote for your country.

Detailed Outline

concurrency
Contents:

Java Concurrency Course

Day 1

  • 1 Introduction
    • Welcome To The Course
      • How we deal with questions
      • Exercises with partial solutions
      • Certificate of Training
    • 1.1 History of concurrency
      • Processes vs Threads vs Virtual Threads
      • Moore's Law and Transistor Count
      • Concurrent vs Parallel Programming
      • Preemptive vs Cooperative vs Virtual Multithreading
      • Java Memory Model
    • 1.2 Benefits of threads
      • Programming is easier
        • Concurrent Programming
        • Parallel Programming
      • Better throughput
      • Simpler modeling
      • More responsive programs
    • 1.3 Risks of threads
      • Safety vs liveness
      • Safety hazards
      • Using basic synchronized
      • Caching of fields
      • Code reordering
      • Annotations for Concurrency
        • Class annotations
        • Field annotations
    • 1.4 Threads are everywhere
      • Timer
      • Servlets and JavaServer Pages
      • Remote Method Invocation (RMI)
      • Swing and AWT
      • HttpClient in Java 11
    • 1.5 Short Java Migration Primer
      • Java 7: underscores, diamond, try-with-resource
      • Java 8: lambdas, streams, compound collection methods
      • Java 11: JPMS, immutable collections, var
      • Java 17: switch expressions, text blocks, records, pattern matching, strong\nencapsulation, sealed classes
      • Java 19-previes: virtual threads
  • 2 Thread Safety
    • Introduction to Thread Safety
      • Synchronization and shared data
      • Your program has latent defects
      • Object orientation and synchronization
      • Example of a simple data race
    • 2.1 What is thread safety?
      • Definition of thread safety
      • Stateless objects always thread-safe
    • 2.2 Atomicity
      • Byte code generated by simple count++
      • Demonstration of broken servlet
      • Lazy initialization check-then-act
      • Reordering of writes
      • Compound actions
        • Check-then-act
        • Read-write-modify
      • Using AtomicLong to keep state safe
      • Request counting with LongAdder
    • 2.3 Locking
      • Mutexes in Java
      • Atomics causing race conditions
      • Monitor locks
      • Effects of coarse-grained locking
      • Reentrancy
    • 2.4 Guarding state with locks
      • Serialized access to critical code
      • Examples of guards with compound actions
      • Making compound actions atomic
      • Synchronizing on non-final fields
      • Synchronizing on Value-Based Classes
      • Locking on monitor lock "this"
      • Maintaining invariants
      • Locking everything is not always enough
      • Synchronization on reads
      • Synchronized and Virtual Threads
    • 2.5 Liveness and performance
      • Coarse-grained vs fine-grained locking
      • Performance difference
      • Simplicity vs performance
  • 3 Sharing Objects
    • 3.1 Visibility
      • Synchronization and visibility
      • Reason why changes are not visible
      • Problems that state data can cause
      • Non-atomic 64-bit numeric operations
      • Making fields visible with volatile
      • Volatile flushing
      • Volatile vs synchronized
      • Single-threaded write safety
      • Volatile Doesn't Guard Against Race Conditions
    • 3.2 Publication and escape
      • Ways we might let object escape
      • Publishing objects via fields
      • Publishing objects as method returns
      • Publishing objects to alien methods
      • Implicit links to outer class
        • Lambdas and method references
      • Safe construction practices
    • 3.3 Thread confinement
      • Unshared objects are safe
      • Thread confinement
        • ThreadLocal in Virtual Threads
      • Stack confinement
      • ThreadLocal
      • Instance confinement
    • 3.4 Immutability
      • Immutable is always thread safe
      • Definition of immutable
      • Immutable containing mutable object
      • Final fields
    • 3.5 Safe publication
      • Making objects and their state visible
      • Safe publication idioms
      • "Effectively" immutable objects
      • How to share objects safely
        • Thread-confined
        • Shared read-only
        • Shared thread-safe
        • Guarded
  • 4 Composing Objects
    • 4.1 Designing a thread-safe class
      • Encapsulation
      • Primitive vs object fields
      • Thread-safe counter with invariant
      • Post-conditions
      • Pre-condition
      • Waiting for pre-condition to become true
    • 4.2 Instance confinement
      • Encapsulation
      • State guarded by private fields
      • Split locks
      • How instance confinement is good
      • Java monitor pattern
      • Lock confinement
      • Example of fleet management
    • 4.3 Delegating thread safety
      • Using thread safe components
      • Delegation with vehicle tracker
      • Delegating safety to ConcurrentMap
      • Independent fieldds
      • Invariables and delegation
        • Using AtomicLong for combining ints
      • Publishing underlying fields
    • 4.4 Adding functionality to existing thread-safe classes
      • Benefits of reuse
      • Modifying existing code
      • Subclassing to add functionality
      • Using composition to add fiunctionality
      • Client-side locking
    • 4.5 Documenting synchronization policies
      • Examples from the JDK
      • What should be documented
      • Synchronization policies
      • Documentation checklist
      • Interpreting vague documentation

Day 2

  • 5 Building Blocks
    • 5.1 Synchronized collections
      • Old Java 1.0 thread-safe containers
      • Synchronized wrapper classes
      • Locking with compound actions
      • Adding functionality to thread-safe classes
      • Exceptions with iteration
      • Hidden iterators
      • Benefits and limitations with "synchronized"
    • 5.2 Concurrent collections
      • Scalability
      • ConcurrentHashMap
      • Additional atomic operations
      • ConcurrentSkipListMap
      • CopyOnWriteCollections
      • ConcurrentLinkedQueue
    • 5.3 Blocking queues and the producer-consumer pattern
      • How BlockingQueues work
      • Timed vs untimed methods
      • Java implementations of BlockingQueue
        • LinkedBlockingQueue
        • ArrayBlockingQueue
          • Circular array lists
        • PriorityBlockingQueue
        • DelayQueue
        • SynchronousQueue
        • TransferQueue
      • Example of producer-consumer
      • Deques
        • ArrayDeque
        • LinkedBlockingDeque
        • ConcurrentLinkedDeque (Java 7)
      • Work stealing
    • 5.4 Blocking and interruptible methods
      • Thread states during blocking
      • Interrupting blocking methods
      • Methods throwing InterruptedException
      • ManagedBlocker
      • Blocking Methods and Virtual Threads
    • 5.5 Synchronizers
      • Coordinating flow of threads
      • Semaphore
      • CountDownLatch
      • AbstractQueuedSynchronizer
      • CyclicBarrier
      • FutureTask
      • Phaser
  • 6 Task Execution
    • 6.1 Executing tasks in threads
      • Identifying tasks
      • Indepence of tasks
      • Task boundaries
      • Single-threaded vs multi-threaded
      • Explicitely creating tasks
      • Disadvantage of unbounded thread creation
      • Creating virtual threads
    • 6.2 Virtual Threads
      • Starting Virtual Threads
      • Virtual Thread Characteristics
      • Carrier Threads
      • Platform Threads State Machine
      • Virtual Threads State Machine
      • Thread.Builder
        • Thread.Builder.OfPlatform
        • Thread.Builder.OfVirtual
    • 6.3 The Executor framework
      • Executor interface
      • Motivation for using Executor
      • Executors Facade pools
      • Decoupling task submission from execution
      • Thread pool benefits
      • Virtual Thread Executor vs Pool
      • Submitting Callable
      • Executor lifecycle, state machine
        • Shutdown() vs ShutdownNow()
        • AutoCloseable (Project Loom)
      • Memory leaks with ThreadLocal
      • Delayed and periodic tasks
        • Difference between java.util.Timer and ScheduledExecutor
    • 6.4 Finding exploitable parallelism
      • Breaking up a single client request
      • Sequential vs parallel
      • Callable and Future
      • Callable controlling lifecycle
      • Example showing page renderer with future
      • Limitations of parallelizing heterogeneous tasks
      • CompletionService
      • Time limited tasks
      • CompletableFuture
        • Chaining of CompletionStages
        • Modeling control flow
        • Using separate pools for stages
        • HttpClient in Java 11
        • CompletableFuture Tips
      • Parallel Request with Virtual Threads
        • Structured Concurrency
        • Managing Error Conditions
        • Timeouts in Requests
    • 6.5 Using Parallel Streams
      • Processing in parallel
      • When to use / avoid
      • Common Fork/Join Pool
  • 7 Cancellation
    • 7.1 Task cancellation
      • Reasons for wanting to cancel a task
      • Cooperative vs preemptive cancellation
      • Using flags to signal cancellation
      • Cancellation policies
      • 7.1.1 Interruption
        • WAITING state of thread
        • Origins of interruptions
        • How does interrupt work?
        • Methods that put thread in WAITING state
        • Policies in dealing with InterruptedException
        • Thread.interrupted() method
        • Interruptions in Virtual Threads
      • 7.1.2 Interruption policies
        • Task vs Thread
        • Different meanings of interrupt
        • Preserving the interrupt status
      • 7.1.3 Responding to interruption
        • Letting the method throw the exception
        • Restoring the interrupt and exiting
        • Ignoring the interrupt status
        • Saving the interrupt for later
      • 7.1.4 Example: timed run
        • Telling a long run to eventually give up
        • Canceling busy jobs
      • 7.1.5 Dealing with non-interruptible blocking
        • Reactions of IO libraries to interrupts
        • Interrupting locks
    • 7.2 Stopping a thread-based service
      • Graceful shutdown
      • Providing lifecycle methods
      • Example: A logging service
      • Asynchronous logging caveats
      • ExecutorService shutdown
      • Poison pills
      • One-shot execution service
    • 7.3 Handling abnormal thread termination
      • Using UncaughtExceptionHandler
      • ThreadGroup for uncaught exceptions
      • Dealing with exceptions in Swing
    • 7.4 JVM shutdown
      • Orderly shutdown
      • Abrupt shutdown
      • Shutdown hooks
      • Daemon threads

Day 3

  • 8 Applying Thread Pools
    • 8.1 Tasks and Execution Policies
      • Homogenous, independent and thread-agnostic tasks
      • Thread starvation deadlock
      • Long-running tasks
    • 8.2 Sizing thread pools
      • Danger of hardcoding worker number
      • Problems when pool is too large or small
      • Formula for calculating how many threads to use
      • CPU-intensiv vs IO-intensive task sizing
      • Examples of various pool sizes
      • Mixing different types of tasks
        • Platform vs Virtual Thread Pools
    • 8.3 Configuring ThreadPoolExecutor
      • corePoolSize, maximumPoolSize, keepAliveTime, workQueue
      • Using default Executors.new* methods
      • Managing queued tasks
      • PriorityBlockingQueue
      • Saturation policies
        • Abort
        • Caller runs
        • Discard
        • Discard oldest
      • Thread factories
    • 8.4 Extending ThreadPoolExecutor
      • Using hooks for extension
      • beforeExecute
      • afterExecute
      • terminate
      • Exceptions from ThreadPoolExecutor
    • 8.5 Parallelizing recursive algorithms
      • Converting sequential tasks to parallel
      • Using Fork/Join to execute tasks
  • 9 Fork/Join
    • Breaking up work into chunks
    • ForkJoinPool and ForkJoinTask
    • Work-stealing in ForkJoinPool
    • ForkJoinTask state machine
    • RecursiveTask vs RecursiveAction
    • Parallel Fibonacci Calculator
    • Fork/Join vs. Compute
    • Parallel merge sort
    • Canceling a task
    • Visibility guarantees with fork/join
    • Placing limits on forking
    • BigInteger.parallelMultiply()
    • Use cases of fork/join
  • 10 Avoiding Liveness Hazards
    • 10.1 Deadlock
      • The drinking philosophers
      • Causing a deadlock amongst philosophers
      • Resolving deadlocks
      • Discovering deadlocks
      • Lock-ordering deadlocks
      • Defining a global ordering
      • Dynamic lock order deadlocks
      • Defining order on dynamic locks
      • Checking whether locks are held
      • Imposing a natural order
      • Deadlock between cooperating objects
      • Open calls and alien methods
        • Example in Vector
      • Resource deadlocks
      • Thread-starvation deadlocks
    • 10.2 Avoiding and diagnosing deadlocks
      • Avoiding multiple locks
      • Using open calls
      • Unit testing for lock ordering deadlocks
      • Adding a sleep to cause deadlocks
      • Verifying thread deadlocks
      • Timed lock attempts
      • Deadlock analysis with thread dumps
      • Thread Dump Caveats
      • Deadlocks in Virtual Threads
    • 10.3 Livelock
  • 11 Testing Concurrent Programs
    • How to test concurrent code
    • BankAccount Example
    • Simple race condition
    • Types of concurrent tests
    • 11.1 Testing for correctness
      • Checking for data races
      • Automatic tooling
        • JChord
        • JavaRaceFinder
        • FindBugs
        • IntelliJ IDEA
        • False positives
      • Testing through bulk updates
      • Server HotSpot interference
      • Testing pitfalls
      • Controlling HotSpot and JIT
      • Turning off optimizations
      • Randomizing bulk operations
      • Testing field visibility
      • Single updates, with time delays
      • Pros and cons of various approaches
      • Examples of testing broken code
      • Testing for deadlocks
    • 11.2 Testing for performance
      • HotSpot tricks
        • Loop unrolling
        • Useless code elimination
        • Inlining of method calls
        • Lock eliding
        • Lock coarsening
        • Eliminating object creation
      • HotSpot interference in microbenchmarks
      • HotSpot method call threshold
      • HotSpot compile time
      • Getting the fastest most optimized code
      • MathRandomTest

Day 4

  • 12 Performance and Scalability
    • 12.1 Thinking about performance
      • Effects of serial sections and locking
      • Performance vs scalability
      • How fast vs how much
      • Evaluating performance tradeoffs
    • 12.2 Amdahl's and Little's laws
      • Formula for Amdahl's Law
      • Utilization according to Amdahl
      • Maximum useful cores
      • Problems with Amdahl's law in practice
      • Formula for Little's Law
      • Applying Little's Law in practice
      • How threading relates to Little's Law
    • 12.3 Costs introduced by threads
      • Cache invalidation
      • Locking and unlocking
      • Memory barriers
    • 12.4 Reducing lock contention
      • Exclusive locks
      • Narrowing lock scope
      • Using ConcurrentHashMap
      • Performance comparisons
      • Reducing lock granularity
      • Lock splitting
        • in LinkedBlockingQueue
        • in ConcurrentLinkedQueue
      • Lock striping
        • in ConcurrentHashMap
        • in LongAdder
      • Alternatives to Exclusive Locks
        • ReadWriteLock
        • StampedLock
        • Concurrent collections
        • CopyOnWrite collections for mostly reads
        • Immutable objects
        • Atomic classes
        • LongAdder and LongAccumulator
      • How to monitor CPU utilization
      • Reasons why CPUs might not be loaded
      • How to find "hot locks"
      • Hotspot options for lock performance
  • 13 Explicit Locks
    • 13.1 Lock and ReentrantLock
      • ReentrantLock implementation
      • Using the owned lock
      • Using try-finally
      • tryLock and timed locks
      • Using try-lock to avoid deadlocks
      • Interruptible locking
    • 13.2 Performance considerations
      • Java 5 vs Java 6 performance
      • Throughput on contended locks
      • Uncontended performance
      • Heavily contended locks
    • 13.3 Fairness
      • Standard non-fair locking
      • Round-robin by OS
      • Barging
      • Fair Owned Locks in Java
      • Throughput of fair locks
    • 13.4 Synchronized vs ReentrantLock
      • Memory semantics
      • Ease of use
      • Prefer synchronized
      • Which to use with Virtual Threads
    • 13.5 Read-write locks
      • ReadWriteLock interface
      • Understanding system to avoid starvation
      • ReadWriteLock vs synchronzied/volatile
      • Up and downgrading locks
    • 13.6 StampedLock
      • What is StampedLock?
      • Pessimistic Exclusive Lock
      • Pessimistic Non-Exclusive Lock
      • Optimistic Non-Exclusive Read (no lock)
      • Writing with a StampedLock
      • Optimistic reading with a StampedLock
  • 14 Building Custom Synchronizers
    • 14.1 Managing state dependence
      • Single-threaded vs multi-threaded
      • Structure of blocking state-dependent actions
      • Example using bounded queues
      • Introducing condition queues
        • With monitor locks
    • 14.2 Using condition queues
      • Condition predicate
      • Lock
      • Condition queue
      • Waking up too soon
      • Missed signals
        • InterruptedException
      • notify() vs notifyAll()
      • Encapsulating condition queues
      • How wait() works for Virtual Threads
    • 14.3 Explicit condition objects
      • Condition interface
      • Benefits of explicit condition queues
      • Timed conditions
    • 14.4 AbstractQueuedSynchronizer (AQS)
      • Basis for other synchronizers
    • 14.4 ManagedBlocker
      • Desired parallelism in ForkJoinPool
      • ManagedUnboundedQueue
      • Where ManagedBlocker is used in JDK
  • 15 Atomic Variables and Nonblocking Synchronization
    • 15.1 Disadvantages of locking
      • Elimination of uncontended intrinsic locks
      • Volatile vs locking performance
      • Priority inversion
      • Volatile flushing
    • 15.2 Hardware support for concurrency
      • Optimistic locking
      • Compare-and-Swap (CAS)
      • Compare-and-Set
      • CAS support in the JVM
      • Managing conflicts with CAS
      • Using "Unsafe" to access memory directly
      • Java 9 VarHandles Instead of Unsafe
      • Shared cache lines
      • Performance advantage of padding
    • 15.3 Atomic variable classes
      • Optimistic locking classes
      • Very fast when not too much contention
      • Types of atomic classes
      • How do atomics work?
      • Atomic array classes
    • 15.4 Nonblocking algorithms
      • Definition of lock-free and wait-free
      • Nonblocking stack
      • Doing speculative work
      • accumulateAndGet()
      • Position with AtomicReference
      • Atomic field updaters
      • Updating with VarHandles
      • compareAndExchange
    • 15.5 Striped64
      • How it works
      • Memory Usage
      • LongAdder
        • Performance
      • LongAccumulator
      • BitUtils for Fast Positions
  • 16 Conclusion
    • Tips on where to learn more
    • Thank you!


About the Author

Heinz Kabutz Java Conference Speaker

Java Champion, author of the Javaspecialists Newsletter, conference speaking regular... About Heinz

Superpack '23

Superpack '23 Our entire Java Specialists Training in one huge bundle more...

Free Java Book

Dynamic Proxies in Java Book

110% Money-back Guarantee

Should you not be satisfied with the quality of the training or the delivery, we will gladly refund you 100% of the course fees. This needs to be brought to our attention within the first 4 hours of the course and a chance should be given to correct whatever you are not satisfied with. If you are still not satisfied, we will refund you 100% of the course fees, plus we will pay our own travel expenses. The training material then remains the property of JavaSpecialists.EU.


Cancellation Policy

If the course is cancelled more than two weeks before the start of the course, a 10% cancellation fee of the fees will apply, plus any non-refundable travel expenses incurred by the trainer.

If the course is cancelled within two weeks of the start of the course, a 50% cancellation fee of the fees will apply, plus any non-refundable travel expenses incurred by the trainer.

No refund will be given to cancellations during the course.


Java Training

We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.

Java Consulting

We can help make your Java application run faster and trouble-shoot concurrency and performance bugs...