Here are the notes I took when Venkat Subramaniam was here for Austin JUG. I will mostly present them as I typed them.
Why Concurrency? Why do we care?
We now have mutli-core processors. Things that work well on one core may not work well on multiple cores.
The Java memory model controls how info goes back and forth from main memory and working memory.
His arguments in his classes are all final.
In the account class. Should the getBalance() be synchronized?
“synchronized” gets a lock and crosses the memory barrier, working and main memory.
Accessing a volatile variable, wait, join, a few other calls where you cross the memory barrier. We could compromise the invariant, like during a constructor.
So if you do not make getBalance synchronized, you might get an old value.
The code could have deadlock, or livelock (you wait, but nothing waits for you).
When you call synchronized, you might wait forever. There is no time out. So you use the Lock interface.
So for ReentrantLock you could call tryLock, or throw the Exception, then you have to unlock it.
You can order the accounts by ID to removed threat of dead lock.
95% of Java code is broken for concurrency. (Is he including code for Groovy, Scala, Clojure?)
Synchronize and suffer model.
Solutions: Software Transactional Memory (from Clojure) and Actors (Scala)
STM: We modify a variable (data) in our code. Shared mutability is bad. A few solutions: Avoid mutability. STM says let’s have a special type of variable: Managed mutable variable. It will only change in certain circumstances: a transaction.
(def balance (ref 0)) (println @balance) ; dereference the reference
Identity: Values are immutable. The stock price at 1:00 is different than at 1:10. But the 1:00 price will never change.
The identity is “google stock price”, points to a different value at different times.
We must protect the identity.
The problem in Java is that many times the problems happen without warning or error message. In STM, you get an error message.
(dosync(ref-set balance 100))
This changes it in a transaction. If there are conflicts, the transaction is aborted and retried. Up to 100,000 times.
Can we do it in Java? He used ref and dosync, so can we use them in Java?
clojure.lang.Ref. Look at his code, see what class he uses for Ref.
private Ref balance; // this was an int
The set method will use Lambda expression in Java 8. For now, we could do a Callable. LockingTransaction.runInTransaction
He is using an inline Callable.call()
What if you try to put more in the to account than is the from amount? It will throw an exception.
When to use STM? When you have multiple readers, multiple writers, and infrequent write collision. Not too many things are good for high-write collision.
You should not modify unmanaged variables in a transaction. Do not write to a file, print out messages, do not send email.
What about a long-running task
Another solution to shared mutability: isolated mutability.
Problem with encapsulation: Many threads can call the methods that change your private variables.
It is like voice mail: Many people can leave him a message, he gets a lot of messages, he goes through them one at a time. Actors have built-in message queue. Asynchronous messages.
ActorSystem.create(); actorSystem.actorOf(new Props(HollywoodActor.class)); pitt.tell("Wonka", null); actorSystem.shutdown();
The Actor runs on a separate thread.
When do actors make sense?
final String PATH = "/usr/bin"; private static int countFiles(final String path) { int count; File file = new File(path); if (file.listFiles() != null) } ActorSystem actorSystem = ActorSystem.create(); MasterCollection master = actorSystem.actorOf(Prop(MasterCollector.class)) master.tell()
The fields are immutable. The messages are not, so they should be immutable.
public void onReceive(final Object message) {}
You can have variables in an Actor, since only one thread will touch an Actor.
When does Actor make sense: Single threaded tasks sequentially
It punishes readers as much as writers. In STM, readers are never blocked. Writers are punished.
Purpose of Actor is not to deal with shared mutability, you want to prevent concurrent access, you want to serialize.
New concurrency API in Java 8.
You need Java 8 with Lambdas.
Look at java.util.streams.*
public static void printHighestPrice( Stream< String > symbols ) { symbols .map( StockUtil::getPrice ) // look that up .filter( stockInfo -> StockUtil.isPriceLessThat( 500 ) ) .reduce( new StockInfo( "", 0.0 ), StockUtil::pickHigh ) }
http://download.java.net/lambda/b78/docs/api/java/util/stream/package-summary.html
:: is a method reference
map(StockUtil::getPrice)
can be done as
map(stock -> StockUtil.getPrice(stock))
Java is getting more functional
Synchronize and shared mutability can be avoided
JDK 5 concurrency is the assembly code of concurrency
Image from the Rheinau Psalter, a 13th century manuscript housed at Central Library of Zurich. Image from e-Codices. This image is assumed to be allowed under Fair Use.