Chapter 7: Concurrency Flashcards
Thread types
System thread: Created by JVM and runs in the background of the application. If thread runs out of memory it generates an Error.
User-defined thread: Created by the developer
Daemon thread: Background thread which won’t stop the program from exiting e.g. garbage collection
Thread scheduler
If more threads created than CPU processors available, a Thread scheduler is used to distribute threads across the processors.
Can execute in a round-robin fashion where each thread receives an equal number of CPU cycles with which to execute. (e.g. gets allotted time to execute and if it doesn’t then it will save the state and perform a context switch).
Can use Thread priority constants to determine which Thread should be run.
e.g. MIN_PROIORITY = 1
NORM_PRIORITY = 5
MAX_PRIORITY = 10
Runnable
Implements void run() method and is a functional interface
Creating a Thread
Note: Java doesn't guarantee when the Thread will actually run. You can either pass a Runnable object or lambda expression to Thread constructor, or can extend Thread and override void run(). e.g. (new Thread(() -> \_\_\_ )).start(); and (new OverriddenThread()).start());
Polling with sleep
Can use Thread.sleep() to let the current Thread sleep while we wait for another Thread to complete.
Executor Service
If more tasks submitted that Thread’s available, they get added to internal queue.
Executors.newSingleThreadExecutor();
Returns a FinalizedDelegatedExecutorService.
Tasks using this service will be performed in order (but is still independent of the main program Thread)
Shutting down a Thread Executor
Must call .shutdown() otherwise program will never end - watch out for questions where executor is not shut down.
Once you’ve shut down the Executor, any new tasks submitted will be rejected and throw RejectedExecutionExcepttion.
.shutDownNow() attempts to immediately stop all running tasks (returns List tasks which were stopped).
Good practice to put in a try finally block and shutdown in a finally block.
Executor Service methods
void execute(Runnable command). This is fire and forget. Future> submit(Runnable task) returns a Future, representing the task. Future submit(Callable task) returns a Future representing pending results. invokeAll(Collection of Callables) executes synchronously + returns collection of futures in original order. This will wait indefinitely for all tasks to complete. invokeAny(Collection of Callables) executes synchronously and returns the result of one of the finished tasks, cancelling unfinished tasks.
Future methods
boolean isDone() -> true is task is completed, cancelled or exception boolean isCancelled() boolean cancel() --> attempts to cancel V get() --> gets results (waits endlessly if not yet available) V get(long timeout, TimeUnit unit). Can throw TimeoutException
Callable
V call() throws Exception .get() returns object of type V (or null)
Exceptions in Runnable and Callable
If lambda expression has a return type in ExecutorService submit() method, it will see it as Callable. So this way it will support a checked Exception, otherwise it won’t support it.
Scheduling Tasks
ScheduleExecutorService
methods: schedule(Callable, long, TimeUnit)
schedule(Runnable, long TimeUnit)
both of these return a ScheduleFuture
scheduleAtFixedRate(Runnable, long initial delay, long delay at fixed rate (regardless of whether previous task finished), TimeUnit)
scheduleWithFixedDelay(Runnable, long initial delay, long delay between termination of one task and start of next, TimeUnit)
Would generate endless Future objects, so can’t use Callable.
Increasing Concurrency with Pools
newSingleThreadExecutor() --> 1 thread results processed in order submitted newSingleThreadScheduledExecutor() --> can delay commands to run after a given delay newCachedThreadPool() --> Thread pool with new threads as needed (will re-use previously constructed threads when they are available). Discouraged to use this as it can take over the application newFixedThreadPool(int nThreads) --> Fixed no. of threads operating on a shared unbounded queue. newScheduledThreadPool(int nThreads)
Synchronising data access
Can use Atomic classes to protect data, which means an operation can be carried out as a single unit of execution without interference from another thread.
e.g. AtomicBoolean
AtomicLong
AtomicLongArray
AtomicInteger
AtomicIntegerArray
AtomicReference –> Reference to an object which can be updated atomically
Note: if not using an Atomic class can’t be sure of result at Runtime
Methods:
.get()
.set()
.getAndSet() sets new value and returns old value
.incrementAndGet() - equivalent to ++ value
.decrementAndGet()
.getAndDecrement().
With Atomic classes, consistency is assumed between data access, but won’t always be in order
Data access in order
Use a monitor (or a lock)
synchronized block.
The first thread there acquires the clock, any subsequent Threads need to wait for the lock to become available.
If you have a method you want to synchronize can either use synchronize(this) or add synchronized keyword by the method signature.
If it’s a static method you want to sychronize, you can use synchronize(MyClass.class), here the class is the monitor, or use the static synchronized in method signature
Remember, to make Thread-safe, all methods in class must be synchronized!!