- Transition to Processing
- Primitive Operations
- Algorithms
- Variables
- Debugging in Processing
- Conditions
- Loops
- Functions
- Scope
- Compound Data
- Reference Semantics
- Refactoring
- Program Design
- Transition to Java
- Debugging in Java
- Unit Testing
- Classes - Writing your own Types
- Classes - Copying objects
- Classes - Functions inside objects
- Classes - Composition
- Classes - Array of objects
- Classes - Class holding array(s)
- Recursion - What goes on during a function call
- Recursion
- Recursion with String data
- Tail-optimized recursion
- Recursion with arrays
- Lists
- Iteration
- List of Lists
- Custom-built ArrayList
- Recursive data structures - 1
- Recursive data structures - 2
- Searching

- Logic and Proofs
- Relations
- Mathematical Functions
- Matrices
- Binary Numbers
- Trigonomtry
- Finite State Machines
- Turing Machines
- Counting - Inclusion/Exclusion
- Graph Algorithms

- Algorithm Efficiency
- Algorithm Correctness
- Trees
- Heaps, Stack, and Queues
- Maps and Hashtables
- Graphs and Graph Algorithms
- Advanced Trees and Computability

- Command line control
- Transition to C
- Pointers
- Memory Allocation
- IO
- Number representations
- Assembly Programming
- Structs and Unions
- How memory works
- Virtual Memory

- Version Control with Git
- Inheritance and Overloading
- Generics
- Exceptions
- Lambda Expressions
- Design Patterns
- Concepts of Concurrency
- Concurrency: Object Locks
- Modern Concurrency

- System Models
- Naming and Distributed File Systems
- Synchronisation and Concurrency
- Fault Tolerance and Security
- Clusters and Grids
- Virtualisation
- Data Centers
- Mobile Computing

- Transition to Scala
- Functional Programming
- Syntax Analysis
- Name Analysis
- Type Analysis
- Transformation and Compilation
- Control Abstraction
- Implementing Data Abstraction
- Language Runtimes

- Transition to Coq
- Proof by Induction, Structured Data
- Polymorphism and Higher-Order Functions
- More Basic Tactics
- Logical Reasoning in Proof Assistants
- Inductive Propositions
- Maps
- An Imperative Programming Language
- Program Equivalence
- Hoare Logic
- Small-Step Operational Semantics
- Simply-typed Lambda Calculus

- Use modern concurrency techniques to solve the problems we discovered earlier

Pages 91 - 111 of “The Well-Grounded Java Developer” by Evans and Verburg.

We have provided for you a *non thread-safe* producer-consumer implementation:

This implementation is not thread-safe because the bounded buffer could be put in an inconsistent state. This inconsistent state will occur if *either* `put`

or `get`

are interrupted in the middle of execution. In particular, if either method is interrupted between adding/removing the new value to array and updating `mNext`

then the buffer will lose or repeat values.

Use one of the techniques you have learned about in the above readings to make this producer-consumer thread-safe without locking it up.

Note, it is quite hard to get the bounded buffer in in inconsistent state - bragging rights go to anyone that can make a program that *always* and *demonstrably* gets into an inconsistent state.

stay tuned…

Imagine you want to add motion blur to an image. Such an algorithm would replace any pixel value with some combination of the pixel values “to the right” of that pixel. So you can experiment with the speed you can get for this type of algorithm so we have provided a `Main.java`

that performs just such a computation.

The code will time itself and report how long it took to run. Your job is to make a multi-threaded version of this code that is faster than the reference implementation above.

Use one of the modern concurrency techniques described in “The Well-Grounded Java Developer” to achieve this as simply as possible.

There are a number of possible solutions. You can consider a countdown latch or you might try an executor service.

stay tuned…