0% found this document useful (0 votes)
165 views

Google Guava Concurrent Slides

This document discusses concurrency in Java and Google's Guava library. It begins with an overview of packages in Guava that relate to concurrency, such as immutable collections, ConcurrentHashMultiset, and Multimaps.synchronizedMultimap. It then covers Java's java.util.concurrent package, including basics of Futures and introducing Google's ListenableFuture. The document emphasizes that ListenableFuture serves as a foundation for higher-level abstractions related to concurrency.

Uploaded by

Faisal Basra
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
165 views

Google Guava Concurrent Slides

This document discusses concurrency in Java and Google's Guava library. It begins with an overview of packages in Guava that relate to concurrency, such as immutable collections, ConcurrentHashMultiset, and Multimaps.synchronizedMultimap. It then covers Java's java.util.concurrent package, including basics of Futures and introducing Google's ListenableFuture. The document emphasizes that ListenableFuture serves as a foundation for higher-level abstractions related to concurrency.

Uploaded by

Faisal Basra
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 39

com.google.common.util.

concurrent
The Guava Team com.google.common.collect com.google.common.util.concurrent java.util.concurrent Effective Java (concurrency is in chapter 10) Java Concurrency in Practice
the linked doc s and books also have advic e about Java c onc urrenc y (general princ iples, java.util.c onc urrent) note: some slides refer to methods that have not yet been released (listeningDec orator, Futures.allAsList/suc c essfulAsList). We expec t to release these in Guava 10 in July

collect
Immutable* ConcurrentHashMultiset Multimaps.synchronizedMultimap MapMaker
before getting to util.c onc urrent, touc h on our other pac kages we keep c onc urrenc y in mind in our other pac kages

collect: Immutable*
whenever possible, for various reasons
use them. linked doc has advantages as well as some tradeoffs one of many reason to use them: thread safety immutable in the thread-safe sense many other c lasses without "Immutable" in the name are immutable (unlike JDK SimpleDateFormat): CharMatc her, Splitter

collect: ConcurrentHashMultiset
Multiset<K> Map<K, Integer> with extra methods and optional thread-safety Map<String, Integer> map = newHashMap(); for (StatsProto proto : protos) { String host = proto.getHost(); if (!map.containsKey(host)) { map.put(host, 1); } else { map.put(host, map.get(host) + 1); } }
c ode c ounts number of times eac h host appears in a proto map from element type to number of oc c urrenc es

collect: ConcurrentHashMultiset
Multiset<K> Map<K, Integer> with extra methods and optional thread-safety Multiset<String> multiset = HashMultiset.create(); for (StatsProto proto : protos) { multiset.add(proto.getHost()); } NumericMap (someday) prefer immutable
e.g., c ount number of queries to eac h shard Numeric Map to support long, double for, e.g., total nanosec onds spent in an operation sometimes need modifiability, e.g., stats (probably from different threads, therefore c onc urrenc y)

collect: Multimaps.synchronizedMultimap
Multimap<K, V> Map<K, Collection<V>> with extra methods and optional thread-safety Map<String, List<StatsProto>> map = newHashMap(); for (StatsProto proto : protos) { String host = proto.getHost(); if (!map.containsKey(host)) { map.put(host, new ArrayList<StatsProto>()); } map.get(host).add(proto); }
c ode indexes protos by host

collect: Multimaps.synchronizedMultimap
Multimap<K, V> Map<K, Collection<V>> with extra methods and optional thread-safety Multimap<String, StatsProto> multimap = ArrayListMultimap.create(); for (StatsProto proto : protos) { multimap.put(proto.getHost(), proto); } synchronized performs better than our internal wait-free equivalent prefer immutable
this partic ular c ode c ould use Multimaps.index() sync hronization espec ially painful with "c hec k then ac t" nec essary for the first item (multiset and multimap both)

collect: MapMaker
slides to come in a few months
build c onc urrent maps and c ac hes

collect: MapMaker
slides to come in a few months
private final ConcurrentMap<String, Feed> feedCache = new MapMaker() .expireAfterWrite(2, MINUTES) .maximumSize(100), .makeMap();
on-demand c omputation with c omputing maps. with a "normal" c ac he, you must look in the c ac he, perform the operation, and insert the result into c ac he. c omputing map hides c ac he management: register a c omputing Func tion, then just c all feedCac he.get

collect: MapMaker: features


concurrent requests to a computing map share the same computation (TODO: sharing without caching) prefer Multiset / Multimap / Table
share c omputation an advantage over manual c ac he management: request LDAP groups for user jsmith c onc urrently in two threads: only one c all is made. Of c ourse, results are c ac hed. Someday MapMaker will allow you to turn off c ac hing so that serves only to c ombine in-flight queries we see people using MapMaker.makeComputingMap() to c reate a Map<K, Collec tion<V>>, Map<K, Integer> etc . don't forget Multiset / Multimap / Table, whic h offer nic er interfac es (Table is a two-keyed Map)

util.concurrent
Future basics ListenableFuture Futures more about Future Service Executor

Future basics
"A handle to an in-progress computation." "A promise from a service to supply us with a result."
different phrasings of the same thing

Future basics
Map<LocalDate, Long> pastSales = archiveService.readSales(); Map<LocalDate, Long> projectedSales = projectionService.projectSales(); return buildChart(pastSales, projectedSales);


we tell a network thread to make a request, and then we bloc k. the network thread sends the request to a server. the server responds to a network thread, and the network thread unbloc ks us repeat ~5s eac h; done in ~10s our thread is doing nothing; it handed off work to another thread/mac hine there's no reason not to overlap the two do-nothing periods, but this c ode c an't

Future basics
inparallel { Map<LocalDate, Long> pastSales = archiveService.readSales(); Map<LocalDate, Long> projectedSales = projectionService.projectSales(); } return buildChart(pastSales, projectedSales);


a possible solution is to modify the language to support an inparallel keyword instead of waiting for the sum of the two operations' durations, we now wait for the max

Future basics
Future<Map<LocalDate, Long>> pastSales = archiveService.readSales(); Future<Map<LocalDate, Long>> projectedSales = projectionService.projectSales(); return buildChart(pastSales.get(), projectedSales.get());


in Java, there's no inparallel, but we c an c hange our methods to return a Future this allows us to split the operation into "make request" and "wait for results" phases the method c alls now make queries but don't wait for their results; when we're done with all our other work, we c all get(), whic h does wait

util.concurrent: ListenableFuture
What Why When How

util.concurrent: ListenableFuture: the what


Future with one new method: addListener(Runnable, Executor) when the future is done (success, exception, cancellation), the listener runs
if the Future is already done at the time of the addListener c all, the listener is invoked when it's added. in short, if you c all addListener, your listener will run as long as future doesn't run forever ListenableFuture implementation responsible for automatic ally invoking when finished works like FutureTask.done, if you're familiar with that exec utor doesn't even see listener until future is done, so (1) there is no overhead or wasted threads and (2) the listener c an c all get() and know it won't wait before, we bloc ked to get the result of a future; now we c an set a c allbac k why would we want to do that? see next slides

util.concurrent: ListenableFuture: the why: callbacks service.process(request).addListener(new Runnable() {


public void run() { try { Result result = Futures.makeUninterruptible(future).get(); // do something with success |result| } catch (ExecutionException ee) { // do something with failure |ee.getCause()| } catch (RuntimeException e) { // just to be safe } } }, executor);
ListenableFuture is a single, c ommon interfac e for both c allbac ks and futures but Runnable isn't the ideal c allbac k interfac e boilerplate: proc ess+addListener, makeUninterruptible, two exc eptions, unwrap in one c ase (yes, some people throw RuntimeExc eption from future.get(), even though perhaps they shouldn't, and if your system is c allbac k based, you'd better c atc h it)

util.concurrent: ListenableFuture: the why: callbacks service.process(new AsyncCallback<Result>() {


public void success(Result result) { // do something with success |result| } public void failure(Exception e) { // do something with failure |e| } }, executor);

We may one day provide an adapter to support this use.


c ompare to GWT c allbac k interfac e not The One Thing that ListenableFuture is good for

util.concurrent: ListenableFuture: the why: "aspects"


future.addListener(new Runnable() { public void run() { processedCount.incrementAndGet(); inFlight.remove(name); lastProcessed.set(name); LOGGER.infofmt("Done with %s", name); } }, executorService);

"aspec ts" in the sense of aspec t-oriented programming, c ode that runs automatic ally every time we do something without inserting that c ode into the main implementation traditional example is that you have an RPC interfac e and want to log every c all. you c ould reimplement the interfac e yourself suc h that every method does two things, log and delegate. or you c ould use guic e or some other interc eptor interfac e to implement one method to be automatic ally invoked whenever a c all is made here you're not the one who is using the output of the future (or at least you're not the primary c onsumer); someone else will c all get() on it later to ac c ess the result This c an work, but it's not the most popular use of ListenableFuture, either.

util.concurrent: ListenableFuture: the why: building blocks


Given several input futures, produce a future that returns the value of the first to complete successfully. Offload postprocessing to another thread.
the killer app: "To serve as a foundation for higher-level abstrac tions" these tasks are examples of things you c an't do with a plain Future (or c an't do effic iently) digression: "first to c omplete suc c esfully" is what Exec utorCompletionServic e does for Futures c reated by submission to an exec utor; that c lass insert c allbac ks in the same plac e as ListenableFuture in the postproc essing example, we want to start postproc essing immediately, so we don't want to kic k off multiple operations, wait for all of them to finish, kic k off multiple postproc essings, wait for all of them to finish, etc . We want postproc essing to start automatic ally when a task c ompletes we'll look at a few libraries that use ListenableFuture later so ListenableFuture is OK for some things, good for others. when to use...?

util.concurrent: ListenableFuture: the when


Always.

util.concurrent: ListenableFuture: the when


Always. (+) Most Futures methods require it. (+) It's easier than changing to ListenableFuture later. (+) Providers of utility methods won't need to provide Future and ListenableFuture variants of their methods. () "ListenableFuture" is lengthier than "Future." () Classes like ExecutorService give you a plain Future by default.
c ost of c reating and passing around ListenableFuture is small you might need it now, or you (or a c aller) might need it later

util.concurrent: ListenableFuture: the how


Create ListenableFuture instead of plain Future: ExecutorService.submit(Callable) Call MoreExecutors.listeningDecorator on your executor. MyFutureTask.set(V) Use SettableFuture.
you need to dec ide that you want a ListenableFuture at *c reation* time we used to have method c alled bloc kAThreadInAGlobalThreadPoolForTheDurationOfTheTask to adapt to ListenableFuture. no, that's not really what it was c alled (really "makeListenable"), but that's how it worked this is nec essarily how any after-the-fac t Future->ListenableFuture c onverter must work, as someone needs to invoke the listeners. by c reating a listener-aware future from the beginning, you're letting the thread that sets the future's value do that makeListenable was a pain when it appeared in tests: it's muc h easier if you c an guarantee that listener runs right away, not when bac kground bloc king thread notic es Most futures ultimately work in one of two ways / two "kinds" of futures (exec utor submission and manual set()); we have utilities to c reate both listeningDec orator to automatic ally make all submissions return ListenableFuture if you are already using your own FutureTask subc lass, subc lass ListenableFutureTask instead or use Abstrac tListenableFuture - *not* Abstrac tFuture

util.concurrent: Futures
transform chain allAsList / successfulAsList others that I won't cover here
I said I'd give some examples of methods operating on futures; here they are

util.concurrent: Futures: transform Future<QueryResult> queryFuture = ...;

Function<QueryResult, List<Row>> rowsFunction = new Function<QueryResult, List<Row>>() { public List<Row> apply(QueryResult queryResult) { return queryResult.getRows(); } }; Future<List<Row>> rowsFuture = transform(queryFuture, rowsFunction);
trivial postproc essing that won't fail: e.g., proto to java objec t the output value is the output of the Func tion, or an exc eption if the original future failed

util.concurrent: Futures: chain


ListenableFuture<RowKey> rowKeyFuture = indexService.lookUp(query); Function<RowKey, ListenableFuture<QueryResult>> queryFunction = new Function<RowKey, ListenableFuture<QueryResult>>() { public ListenableFuture<QueryResult> apply(RowKey rowKey) { return dataService.read(rowKey); } }; ListenableFuture<QueryResult> queryFuture = chain(rowKeyFuture, queryFunction);
c hain() is transform() on steroids: the transformation c an fail with a c hec ked exc eption, and it c an be performed async hronously heavy, multi-stage queries, like an index lookup + data lookup the work done during the original listenablefuture is the first step; the func tion derives the sec ond step from the result of the first the output value is the output of the Future returned by the Func tion, or an exc eption if the original future failed you c ould perform the stages in series yourself, but you might want multiple suc h c hains of exec ution to oc c ur in parallel, so you need to keep everything in terms of Future

util.concurrent: Futures: allAsList / successfulAsList


List<Future> Future<List> Difference is exception policy: allAsList fails if any input fails successfulAsList succeeds, with null in place of failures

util.concurrent: He's still talking about Future


Don't implement it yourself. Avoid: deadlocks data races get() that returns different values at different times get() that throws RuntimeException extra calls to listeners conflating two kinds of cancellation Remember: MoreExecutors.listeningDecorator, SettableFuture.
I c ouldn't write a c orrec t Future implementation from sc ratc h without a lot of researc h. even if I did, it would be slow you might think it would be hard to write a future that returns different values at different times, but I edited some c ode that had this behavior and didn't notic e that it worked that way until I ran the tests after my c hange and found that they failed RuntimeExc eption breaks c allbac ks that c all get() and don't c hec k for it Future c anc ellation is a talk unto itself; it's probably not a big deal if your c anc el() implementation is broken, but why not get it right for free? also oc c asionally useful are Abstrac tListenableFuture and ListenableFutureTask

util.concurrent: He's still talking about Future


Don't even mock it (usually). Don't Mock Data Objects (aka Value Objects). "Data object?" Future is more Queue than List or proto. Contrast to service objects: Fragile: accessed by get (timed or untimed), addListener, or isDone vs. a service object with only one method per operation (usually) Lightweight: immediateFuture(userData) vs. a test Bigtable with the user data
examples: a protoc ol buffer is a value objec t; an rpc stub is a servic e objec t is Future, with all its behavior, really a data objec t? more or less think of it like Queue, whic h is a c onc urrent c ollec tion but still a c ollec tion. even List has behavior. only some data c lasses have none whatsoever moc king Future: you don't want to, and you don't need to fragility: state is ac c essed by multiple methods: multiple get()s, or peek()/poll()/.... a c hange in implementation of c hain() to ac c ess that state in different ways c ould break your moc ks by c ontrast, while servic e objec ts c ertainly *c an* have multiple methods that do the same thing (e.g., sync and async RPC variants), this oc c urs less often "immediateFuture: it's easier than setting up a Bigtable test c ell"

util.concurrent: Executor
MoreExecutors.sameThreadExecutor for quick tasks that can run inline MoreExecutors.getExitingExecutorService for "half-daemon" threads UncaughtExceptionHandlers.systemExit for exiting after unexpected errors ThreadFactoryBuilder new ThreadFactoryBuilder() .setDaemon(true) .setNameFormat("WebRequestHandler-%d") .build();
plus others I won't c over here use sameThreadExec utor only when you literally don't c are whic h thread runs the task; think of it as "anyThreadExec utor" don't get c ute, e.g. "my future's value c omes from a task in thread pool X, so if I use sameThreadExec utor, my listener will run in that thread pool." that's *usually* true, but if your addListener c all oc c urs *after* the future c ompletes, now your listener is running in the thread that invoked addListener instead don't let me sc are you away from sameThreadExec utor entirely, but reserve it for fast tasks only "half-daemon" solves the following problem with important bac kground tasks: if your threads are non-daemon, the proc ess c an't exit automatic ally; if they're daemon, the proc ess will exit without waiting for them to finish getExitingExec utorServic e threads keep the VM alive only as long as they are doing something one c onfiguration option you have when setting up a thread pool is what to do with unhandled exc eptions. by default, they're printed to stderr (not your logs), and the thread (not the proc ess) dies, whic h might be bad if thread is important; maybe you don't know what it was in the middle of another option in setting up thread pool is to set other properties of individual threads; to help, we provide ThreadFac toryBuilder

util.concurrent: Service
definition lifecycle implementation

util.concurrent: Service: definition


"An object with an operational state, plus asynchronous start() and stop() lifecycle methods to transfer into and out of this state." web servers, RPC servers, monitoring initialization, ...
anything you might start when starting your proc ess and maybe take down at the end the value of the interfac e is the c ommon API for us to adapt all our servic es to, hiding threading, etc .; the implementation c ould c hange how it uses threads without affec ting users this frees users from thinking about these details and it allows users to build libraries that c an run atop any generic Servic e this puts the burden of handling threading on the Servic e implementer, but we simplify things for them with helper c lasses. More on that in a minute

util.concurrent: Service: lifecycle


States: NEW STARTING RUNNING STOPPING TERMINATED
Note the lac k of restart - it's a one way street. It's bec ause our Servic e ac tually models a 'servic e invoc ation'. you c an trigger start/stop and optionally wait for them to c omplete with... a ListenableFuture

util.concurrent: Service: implementation


AbstractExecutionThreadService AbstractIdleService AbstractService
c hoose an implementation based on how your servic e does its threading: single-threaded, multi-threaded, and arbitrarily threaded, respec tively I'll show a sample implementation using eac h

util.concurrent: Service: AbstractExecutionThreadService protected void startUp() {


} protected void run() { Connection connection; while ((connection = queue.take() != POISON)) { process(connection); } } protected void triggerShutdown() { dispatcher.stopListeningForConnections(queue); queue.put(POISON); }
start() c alls your startUp() method, c reates a thread for you, and invokes run() in that thread. stop() c alls triggerShutdown() and waits for the thread to die

dispatcher.listenForConnections(port, queue);

util.concurrent: Service: AbstractIdleService protected void startUp() {


} protected void shutDown() {} servlets.add(new GcStatsServlet());
for when you need a thread only during startup and shutdown (here, any queries to the Gc StatsServlet already have a request thread to run in)

util.concurrent: Service: AbstractService protected void doStart() { protected void doStop() {


new Thread("webserver") { public void run() { try { notifyStarted(); webserver.run(); } catch (Exception e) { notifyFailed(e); } } }.start();

new Thread("stop webserver") { public void run() { try { webserver.blockingShutdown(); notifyStopped(); } catch (Exception e) { notifyFailed(e); } } }.start();

for servic es that require full, manual thread management here, we need manual thread management bec ause our webserver doesn't have an async hronous stopAndNotifyCallbac k method. stop() isn't allow to bloc k, so our doStop() kic ks off its own thread if not for that, we c ould use Abstrac tExec utionThreadServic e, with the c ontents of doStart() moved to run() the lac k of an async hronous shutdown method is exac tly the kind of annoyanc e that Servic e exists to paper over

Questions?
Bugs Usage (use the tag guava) Discussion

You might also like