Slide navigation: Forward with space bar, → arrow key, or PgDn. Backwards with ← or PgUp.
ExecutorService
to map tasks to threads
ExecutorService service = Executors.newCachedThreadPool();or
int processors = Runtime.getRuntime().availableProcessors(); ExecutorService service = Executors.newFixedThreadPool(processors);
Runnable
Runnable
has one method
void run()
run
methodRunnable
object to ExecutorService
Runnable task = () -> { task instructions }; service.execute(task)
public static Runnable greeter(String greeting, int repetitions) { return () -> { for (int i = 0; i < repetitions; i++) System.out.println(greeting); }; }
Runnable
instances
Runnable r1 = greeter("Hello", 100); Runnable r2 = greeter("Goodbye", 100);
ExecutorService
ExecutorService service = Executors.newCachedThreadPool(); service.execute(r1); service.execute(r2); service.shutdown();
Hello Hello Hello Goodbye ... Goodbye Goodbye Hello ...
Callable<V>
for a task that yields a result
public interface Callable<V> { V call() throws Exception; }
Callable
by submitting it to an ExecutorService
Callable<V> task = . . .; Future<V> resultFuture = service.submit(task);
Future<V>
—an object that will yield a value in the future.get
method blocks until value is available:
V result = resultFuture.get()
List<Callable<V>> tasks = . . .;
List<Future<V>> resultFutures = service.invokeAll(tasks);
for (Future<V> resultFuture : resultFutures)
{
V result = resultFuture.get();
Incorporate result
in the final result
}
try { V result = service.invokeAny(tasks); . . . } catch (ExecutionException ex) { // No task yielded a result }
run
exitsstop
methodinterrupt
t.interrupt()
doesn't actually interrupt t
; just sets a flag
run
method
Thread.currentThread().isInterrupted()
sleep
, wait
throw InterruptedException
when thread interruptedInterruptedException
and react to interruptionrun
when sensing interruptionpublic class MyRunnable implements Runnable { public void run() { try { while (...) { do work Thread.sleep(...); } } catch (InterruptedException e) { // terminate thread } } }
for (int i = 0; i < repetitions; i++) queue.add(greeting);
int count1 = 0; int count2 = 0; for (int i = 0; i < repetitions; i++) { String greeting = queue.remove(); if (greeting1.equals(greeting)) count1++; else if (greeting2.equals(greeting)) count2++; } System.out.println(greeting1 + ": " + count1); System.out.println(greeting2 + ": " + count2);
public static void main(String[] args) { int n = 1000; BoundedQueue<String> q = new BoundedQueue<>(2 * n); ExecutorService service = Executors.newCachedThreadPool(); Runnable p1 = producer("Hello", q, n); Runnable p2 = producer("Goodbye", q, n); Runnable c = consumer("Hello", "Goodbye", q, 2 * n); service.execute(p1); service.execute(p2); service.execute(c); service.shutdown(); }
Hello: 1000 Goodbye: 1000
Hello: 487 Goodbye: 807
Why is the program corrupted?
add
and executes
elements[tail] = anObject;
add
and executes
elements[tail] = anObject;
tail++;
tail++;
java.util.concurrent.Lock
interface type, usually ReentrantLock
aLock = new ReentrantLock(); . . . aLock.lock(); try { protected code } finally { aLock.unlock(); }
add
and acquires lock, then executes
elements[tail] = anObject;
add
and tries to acquire lock, but it is blocked
tail++;
add
, releases lock
add
, remove
if (!queue.isFull()) queue.add(...);
can still be interruptedadd
method
public void add(E newValue) { queueLock.lock(); try { while (queue is full) wait for more space . . . } finally { qeueLock.unlock(); } }
remove
private Lock queueLock = new ReentrantLock(); private Condition spaceAvailableCondition = queueLock.newCondition();
await
when condition is not fulfilled:
public void add(E newValue) { . . . while (size == elements.length) spaceAvailableCondition.await(); . . . }
signalAll
on the same condition objectpublic E remove() { . . . E r = elements[head]; . . . spaceAvailableCondition.signalAll(); // Unblock waiting threads return r; }
synchronized
method acquires lock of implicit parametersynchronized
method releases lockLock
objects
public class BoundedQueue<E> { public synchronized void add(E newValue) { . . . } public synchronized E remove() { . . . } . . . }
Object.wait
blocks current thread and adds it to wait set
Object.notifyAll
unblocks waiting threads
public synchronized void add(E newValue) throws InterruptedException { while (size == elements.length) wait(); elements[tail] = anObject; . . . notifyAll(); // notifies threads waiting to remove elements }
synchronized (obj) { critical section }
Map<String, Integer> map = . . . synchronized (map) { Integer value = map.get(key); if (value == null) map.put(key, 1); else map.put(key, value + 1); }
ConcurrentHashMap
insteadBoundedQueue
in productionjava.util.concurrent
has professionally implemented threadsafe classesArrayBlockingQueue
(bounded), LinkedBlockingQueue
(unbounded)BlockingQueue
interfaceput
, take
block if full/emptyadd
, remove
throw exception if full/empty offer
, poll
return error code if full/empty (less useful in concurrent programs)compare
methodComparator<Double> comp = (d1, d2) -> { sleep return comparison result };
java.util.concurrent
"Run"
or "Step"
to queuetake
on the queue, blocks if no string inserted