Concurrency Flashcards
thread
the smallest unit of execution that can be scheduled by the operating system
process
a group of associated threads that execute in the same, shared environment
Shared Environment
the threads in the same process share the same memory space and can communicate directly with one another
Concurrency
The property of executing multiple threads and processes at the same time
Context Switch
the process of storing a thread’s current state and later restoring the state of the thread to continue execution
thread priority
a numeric value associated with a thread that is taken into consideration by the thread scheduler when determining which threads should currently be executing
Values of
Thread.MIN_PRIORITY
Thread.NORM_PRIORITY
Thread.MAX_PRIORITY
1
5
10
Note: If two threads have the same priority, the thread scheduler will arbitrarily choose the one to process first in most situations.
Runnable
a functional interface that takes no arguments and returns no data.
commonly used to de ne the work a thread will execute, separate from the main application thread.
@FunctionalInterface public interface Runnable { void run();
}
How to execute a task with the Thread class
First you define the Thread with the corresponding task to be done. Then you start the task by using the Thread.start() method.
Two ways to define the task for a Thread instance
1) Provide a Runnable object or lambda expression to the Thread constructor (preferred method):
public class PrintData implements Runnable { public void run() { //print something } public static void main(String[] args) { (new Thread(new PrintData())).start(); } }
2) Create a class that extends Thread and overrides the run() method:
public class ReadInventoryThread extends Thread { public void run() { System.out.println("Printing zoo inventory"); } public static void main(String[] args) { (new ReadInventoryThread()).start(); } }
What order are threads processed in?
Whatever the thread scheduler decides
There is no guarantee that the thread you start first will be processed before the second. They could be broken up over time switching between threads too. Shit’s nuts and unpredictable.
A reason to prefer extending Thread instead of implementing Runnable when creating your own thread
If you need to define your own Thread rules upon which multiple tasks will rely, such as a priority Thread, extending Thread may be preferable.
3 reasons to prefer implementing Runnable instead of extending Thread when creating your own thread
1) Implementing Runnable lets you extend another class.
2) Implementing Runnable is often a better object-oriented design practice since it separates the task being performed from the Thread object performing it.
3) Implementing Runnable allows the class to be used by numerous Concurrency API classes.
Polling
the process of intermittently checking data at some fixed interval
Thread.sleep(long mills)
Requests the current thread of execution rest for a speci ed number of milliseconds. When used inside the body of the main() method, the thread associated with the main() method will pause, while the separate thread will continue to run.
What exception might Thread.sleep() throw?
InterruptedException
ExecutorService
An interface that includes numerous useful features, such as thread pooling and scheduling, which would be cumbersome for you to implement in every project. Therefore, it is recommended that you use this framework anytime you need to create and execute a separate task, even if you need only a single thread.
How to create an instance of ExecutorService for a single thread and execute the thread. Then close the ExecutorService.
ExecutorService service = null; try { service = Executors.newSingleThreadExecutor();
service.execute(() -> System.out.println("Running")); } finally { service.shutdown(); }
If a single-thread ExecutorService instance is used to execute multiple threads, what order will they be executed in?
The order in which they are added to the ExecutorService
What will happen if you don’t call shutdown() on your executor?
Your application will never terminate.
RejectedExecutionException
Thrown when a new task is submitted to the thread executor while it is shutting down
isShutdown() and isTerminated()
isShutdown() will return true while an executor is being shut down and after it shuts down
isTerminated() will return false while an executor is being shut down and return true after it shuts down
Difference between shutdown() and shutdownNow()
shutdown() will finish executing any tasks that have already been submitted to the executor
shutdownNow() will attempt to cancel any currently running tasks or tasks that are queued up. Also returns a List of tasks that were submitted to the thread executor but that were never started.
void execute(Runnable command)
Executes a Runnable task at some point in the future
Future> submit(Runnable task)
Executes a Runnable task at some point in the future and returns a Future representing the task
Future submit(Callable task)
Executes a Callable task at some point in the future and returns a Future representing the pending results of the task
List> invokeAll( Collection extends Callable> tasks)
Executes the given tasks, synchronously returning the results of all tasks as a Collection of Future objects, in the same order they were in the original collection
T invokeAny( Collection extends Callable> tasks)
Executes the given tasks, synchronously returning the result of one of finished tasks, cancelling any unfinished tasks
Which method is preferred, submit() or execute()?
submit()
Mostly just cuz they basically do the same thing but submit returns a Future object that can be used
Future method
boolean isDone()
Returns true if the task was completed, threw an exception, or was cancelled
Future method
boolean isCancelled()
Returns true if the task was cancelled before it completely normally.
Future method
boolean cancel()
Attempts to cancel execution of the task.
Future method
V get()
Retrieves the result of a task, waiting endlessly if it is not yet available.
Future method
V get(long timeout, TimeUnit unit)
Retrieves the result of a task, waiting the specified amount of time. If the result is not ready by the time the timeout is reached, a checked TimeoutException will be thrown.
Callable interface
similar to Runnable except that its call() method returns a value and can throw a checked exception.
@FunctionalInterface public interface Callable { V call() throws Exception; }
ambiguous lambda expression
When the compiler is unable to assign a functional interface to a lambda expression
awaitTermination(long timeout, TimeUnit unit)
Call this method on an ExecutorService object to wait a specified amount of time before continuing on, or it will return earlier if the tasks finish before the specified amount of time
ScheduledExecutorService
A subinterface of ExecutorService that allows us to schedule a task for the future, possibly repeatedly if wanted.
How to create an instance of ScheduledExecutorService for a single thread
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
ScheduledExecutorService Method
schedule(Callable callable, long delay, TimeUnit unit)
Creates and executes a Callable task after the given delay
ScheduledExecutorService Method
schedule(Runnable command, long delay, TimeUnit unit)
Creates and executes a Runnable task after the given delay
ScheduledExecutorService Method
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
Creates and executes a Runnable task after the given initial delay, creating a new task every period value that passes.
ScheduledExecutorService Method
scheduleAtFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
Creates and executes a Runnable task after the given initial delay and subsequently with the given delay between the termination of one execution and the com- mencement of the next
thread pool
a group of pre-instantiated reusable threads that are available to perform a set of arbitrary tasks
Executors method
newCachedThreadPool()
Creates a thread pool that creates new threads as needed, but will reuse previ- ously constructed threads when they are available.
Returns an ExecutorService object
Executors method
newFixedThreadPool(int nThreads)
Creates a thread pool that reuses a fixed number of threads operating off a shared unbounded queue.
Returns an ExecutorService object
Executors method
newScheduledThreadPool(int nThreads)
Creates a thread pool that can schedule commands to run after a given delay or to execute periodically.
Returns a ScheduleExecutorService object
race condition
the unexpected result of two tasks executing at the same time
Atomic
the property of an operation to be carried out as a single unit of execution without any interference by another thread
What are the Atomic classes I need to know?
1) AtomicBoolean
2) AtomicInteger
3) AtomicIntegerArray
4) AtomicLong
5) AtomicLongArray
6) AtomicReference
7) AtomicReferenceArray
Atomic methods
get()
set()
getAndSet()
incrementAndGet()
getAndIncrement()
decrementAndGet()
getAndDecrement()
get():
Retrieve the current value
set():
Set the given value, equivalent to the assignment = operator
getAndSet():
Atomically sets the new value and returns the old value
incrementAndGet():
For numeric classes, atomic pre-increment operation equivalent to ++value
getAndIncrement():
For numeric classes, atomic post-increment operation equivalent to value++
decrementAndGet():
For numeric classes, atomic pre-decrement operation equivalent to –value
getAndDecrement():
For numeric classes, atomic post-decrement operation equivalent to value–
Synchronized Block
Notated by the synchronized keyword, they are used to make sure that the operations in the synchronized block will only be run by one thread at a time.
synchronized(this) { // Work to be completed by one thread at a time }
Note: you can synchronize on any object
synchronized method modifier
Does the same thing as if the entire method content were wrapped in a synchronized block
BlockingQueue method
offer(E e, long timeout, TimeUnit unit)
Adds item to the queue waiting the specified time, returning false if time elapses before space is available
Throws InterruptedException if interrupted
BlockingQueue method
poll(long timeout, TimeUnit unit)
Retrieves and removes an item from the queue, waiting the specified time, returning null if the time elapses before the item is available
Throws InterruptedException if interrupted
LinkedBlockingQueue vs. LinkedBlockingDeque
LinkedBlockingDeque maintains a doubly-linked list and has offer and poll methods to deal with the first and last elements efficiently.
ConcurrentSkipListSet and ConcurrentSkipListMap
Pretty much just concurrent versions of their TreeSet and TreeMap, respectively.
They maintain their elements or keys in the natural ordering of their elements.
CopyOnWriteArrayList and CopyOnWriteArraySet
These classes copy all of their elements to a new underlying structure anytime an element is added, modi ed, or removed from the collection. Although the data is copied to a new underlying structure, our reference to the object does not change.
What is the output here? Why?
List list = new CopyOnWriteArrayList<>(Arrays.asList(4,3,52)); for(Integer item: list) {
System.out.print(item+” “);
list.add(9); }
System.out.println(); System.out.println(“Size: “+list.size());
4 3 52
Size: 6
With CopyOnWrite collections, any iterator established prior to a modification will not see the changes, but instead it will iterate over the original elements prior to the modification.
How to obtain a synchronized collection from a regular collection
The Concurrency API provides methods for converting Lists/Maps/Sets/Collections using something like
List list = new ArrayList<>(Arrays.asList(3,4,5));
list = Collections.synchronizedList(list)
What is the thing to watch for when using the synchronized collections?
They do not synchronize access on any iterators that you may create from the synchronized collection. Therefore, it is imperative that you use a synchronization block if you need to iterate over any of the synchronized collections
parallel stream
a stream that is capable of processing results concurrently, using multiple threads.
How to create a parallel stream from an existing stream
Use the parallel() intermediate operation
Stream stream = Arrays.asList(1,2,3,4,5,6).stream(); Stream parallelStream = stream.parallel();
How to create a parallel stream from a collection
Use the parallelStream() operation
Stream parallelStream2 = Arrays.asList(1,2,3,4,5,6).parallelStream();
stateful lambda expression
One whose result depends on any state that might change dur- ing the execution of a pipeline.
Avoid these in parallel streams
3 Requirements for Parallel Reduction with collect()
1) The stream is parallel.
2) The parameter of the collect operation has the Collector.Characteristics.CONCURRENT characteristic.
3) Either the stream is unordered, or the collector has the characteristic Collector.Characteristics.UNORDERED.
3 Requirements for reduce() arguments on parallel streams
1) The identity must be defined such that for all elements in the stream u, combiner.apply(identity, u) is equal to u.
2) The accumulator operator op must be associative and stateless such that (a op b) op c is equal to a op (b op c).
3) The combiner operator must also be associative and stateless and compatible with the identity, such that for all u and t combiner.apply(u,accumulator.apply(identity,t)) is equal to accumulator.apply(u,t).
What are the two Collectors methods that produce an unordered and concurrent collection
Collectors.toConcurrentMap
Collectors.groupingByConcurrent
CyclicBarrier
Takes in its constructors a limit value, indicating the number of threads to wait for. As each thread finishes, it calls the await() method on the cyclic barrier. Once the specified number of threads have each called await(), the barrier is released and all threads can continue.
How many times can you use a CyclicBarrier object?
As many times as you need
If its limit is 5 and you need to work with 15 threads for example, you can use the same CyclicBarrier object three times.
What are the two classes you can extend to implement a fork/join solution?
RecursiveAction and RecursiveTask
both of which implement the ForkJoinTask interface
What method must you implement for RecursiveAction and RecursiveTask?
void compute() for RecursiveAction
T compute() for RecursiveTask
Once you define your task class for the fork/join solution, how do you start it?
(assume the task class is called Weigh)
ForkJoinTask> task = new Weigh(); ForkJoinPool pool = new ForkJoinPool(); pool.invoke(task);
invokeAll method
takes two instances of the fork/join class and does not return a result.
fork() method
causes a new task to be submitted to the pool and is similar to the thread executor submit() method.
join() method
called after the fork() method and causes the current thread to wait for the results of a subtask.
In what order should you call the methods for the fork/join framework?
fork()
compute()
join()
Deadlock
when two or more threads are blocked forever, each waiting on the other.
Starvation
when a single thread is perpetually denied access to a shared resource or lock.
Livelock
when two or more threads are conceptually blocked forever, although they are each still active and trying to complete their task