1. Chicago, October 19 - 22, 2010
Dr Paul King, @paulk_asert
paulk at asert.com.au
Concurrency with GPars
2. "Andy giveth and Bill taketh away"
GPars - 2
Source:HerbSutter:http://www.gotw.ca/publications/concurrency-ddj.htm
3. Why is it hard?
• Many issues to deal with:
– Doing things in parallel, concurrently,
asynchronously
• Processes, Threads, Co-routines, Events, Scheduling
– Sharing/Synchronization Mechanisms
• shared memory, locks, transactions, wait/notify, STM,
message passing, actors, serializability, persistence,
immutability
– Abstractions
• Shared memory on top of messaging passing
• Message passing on top of shared memory
• Dataflow, Selective Communication, Continuations
– Data Structures and Algorithms
• Queues, Heaps, Trees
• Sorting, Graph Algorithms
GPars - 3
4. GPars - 4
Java Concurrency Features
• The early years
– Threads, synchronised and non-synchronised
collections, synchronisation at the language level,
Monitors (wait/notify), Locks, ThreadLocal, final, ...
• More recent enhancements
– java.util.concurrent: Executors, Thread Pools,
Optimistic updates, Blocking queues, Synchronizers,
Callables, Futures, Atomic operations, Deques, ...
• Emerging
– Fork/Join & others, Kilim, Phasers, PicoThreads ...
• Leverage related APIs/technologies
– Networking, real-time, GUIs, simulation, database,
multimedia, operating systems, parallel processing,
distribution, mobile agents, nio, ...
5. Java Concurrency Best Practice?
• Java Concurrency in Practice:
–“If mutable threads access the
same mutable state variable
without appropriate
synchronization,
your program is broken”
–“When designing thread-safe classes,
good object-oriented techniques –
encapsulation, immutability, and clear
specification of invariants – are your
best friends”
GPars - 5
32. Lightweight threads: Jetlang
• Jetlang
– A high performance threading library
– http://code.google.com/p/jetlang/
GPars - 32
import org.jetlang.fibers.ThreadFiber
import org.jetlang.core.Callback
import org.jetlang.channels.MemoryRequestChannel
import org.jetlang.channels.AsyncRequest
def req = new ThreadFiber() // or pool
def reply = new ThreadFiber()
def channel = new MemoryRequestChannel()
req.start()
reply.start()
channel.subscribe(reply, { it.reply(it.request.sum()) } as Callback)
AsyncRequest.withOneReply(req, channel, [3, 4, 5], { println it } as Callback)
sleep 1000
req.dispose()
reply.dispose()
33. Other High-Level Libraries: JPPF
– Open source Grid Computing platform
– http://www.jppf.org/
GPars - 33
import org.jppf.client.*
import java.util.concurrent.Callable
class Task implements Callable, Serializable {
private static final long serialVersionUID = 1162L
public Object call() {
println 'Executing Groovy'
"Hello JPPF from Groovy"
}
}
def client = new JPPFClient()
def job = new JPPFJob()
def task = new Task()
job.addTask task
def results = client.submit(job)
for (t in results) {
if (t.exception) throw t.exception
println "Result: " + t.result
}
34. Other High-Level Libraries: Gruple...
– http://code.google.com/p/gruple
– Simple abstraction to coordinate and synchronize
threads with ease – based on Tuplespaces
• Tuplespaces provide the illusion of a shared memory on top
of a message passing system, along with a small set of
operations to greatly simplify parallel programming
– Example Tuple:
[fname:"Vanessa", lname:"Williams", project:"Gruple"]
– Basic operations within a Tuplespace are:
• put - insert a tuple into the space
• get - read a tuple from the space (non-destructively)
• take - take a tuple from the space (a destructive read)
– Further reading: Eric Freeman, Susanne Hupfer, and
Ken Arnold. JavaSpaces Principles, Patterns, and
Practice, Addison Wesley, 1999
GPars - 34
43. …Multiverse STM
GPars - 43
class Account {
private final balance = new LongRef()
Account(long initial) {
balance.set initial
}
void setBalance(long newBalance) {
if (newBalance < 0)
throw new RuntimeException("not enough money")
balance.set newBalance
}
long getBalance() {
balance.get()
}
}
44. Testing multi-threaded applications: ConTest...
• Advanced Testing for Multi-Threaded Applications
– Tool for testing, debugging, and coverage-measuring
of concurrent programs (collects runtime statistics)
– Systematically and transparently (using a java agent)
schedules the execution of program threads in ways
likely to reveal race conditions, deadlocks, and other
intermittent bugs (collectively called synchronization
problems) with higher than normal frequency
– The ConTest run-time engine adds heuristically
controlled conditional instructions (adjustable by a
preferences file) that force thread switches, thus
helping to reveal concurrent bugs. You can use
existing tests and run ConTest multiple times – by
default different heuristics used each time it is run
• http://www.alphaworks.ibm.com/tech/contest
GPars - 44
46. GPars - 46
GContracts
@Grab('org.gcontracts:gcontracts:1.0.2')
import org.gcontracts.annotations.*
@Invariant({ first != null && last != null })
class Person {
String first, last
@Requires({ delimiter in ['.', ',', ' '] })
@Ensures({ result == first + delimiter + last })
String getName(String delimiter) {
first + delimiter + last
}
}
new Person(first: 'John', last: 'Smith').getName('.')
1.8+
47. Testing: Spock
GPars - 47
class HelloSpock extends spock.lang.Specification {
def "length of Spock's and his friends' names"() {
expect:
name.size() == length
where:
name | length
"Spock" | 5
"Kirk" | 4
"Scotty" | 6
}
}
95. More Information about Concurrency
• Web sites
– http://gpars.codehaus.org/
– http://g.oswego.edu/
Doug Lea's home page
– http://gee.cs.oswego.edu/dl/concurrency-interest/
– http://jcip.net/
Companion site for Java Concurrency in Practice
– http://www.eecs.usma.edu/webs/people/okasaki/pubs.html#cup98
Purely Functional Data Structures
– http://delicious.com/kragen/concurrency
Concurrency bookmark list
– http://www.gotw.ca/publications/concurrency-ddj.htm
The Free Lunch is Over, Herb Sutter
– http://manticore.cs.uchicago.edu/papers/damp07.pdf
– http://mitpress.mit.edu/catalog/item/default.asp?ttype=2&tid=10142
Concepts, Techniques, and Models of Computer Programming
GPars - 95
96. More Information about Groovy
• Web sites
– http://groovy.codehaus.org
– http://grails.codehaus.org
– http://pleac.sourceforge.net/pleac_groovy (many examples)
– http://www.asert.com.au/training/java/GV110.htm (workshop)
• Mailing list for users
– user@groovy.codehaus.org
• Information portals
– http://www.aboutgroovy.org
– http://www.groovyblogs.org
• Documentation (1000+ pages)
– Getting Started Guide, User Guide, Developer Guide, Testing
Guide, Cookbook Examples, Advanced Usage Guide
• Books
– Several to choose from ...
GPars - 96
100. …Multiverse Philosophers
GPars - 100
class Philosopher implements Runnable {
String name
Fork left, right
IntRef timesEaten = new IntRef()
IntRef food
void eat() {
atomic(trackreads: true, explicitRetryAllowed: true) {
left.free.await(true)
right.free.await(true)
if (food.get() > 0) {
left.take(); right.take()
timesEaten.inc(); sleep 10; food.dec()
}
}
}
void think() {
atomic(trackreads: true, explicitRetryAllowed: true) {
left.release(); right.release()
}
sleep 10
}
void run() { 10.times { eat(); think() } }
String toString() {
switch (timesEaten) {
case 0: return "$name has starved"
case 1: return "$name has eaten once"
default: return "$name has eaten $timesEaten times"
}
}
}
101. Jetlang Philosophers…
GPars - 101
import org.jetlang.core.Callback
import org.jetlang.fibers.ThreadFiber
import org.jetlang.channels.*
def names = ['socrates', 'plato', 'aristotle', 'descartes', 'nietzsche']
class Philosopher implements Callback {
private random = new Random()
String name
int timesEaten = 0
String status
def forks
private channels = [new MemoryRequestChannel(), new MemoryRequestChannel()]
private req = new ThreadFiber() // or from pool
private reply = new ThreadFiber()
private responses = []
private gotFork = { it instanceof Accepted }
void start() {
assert forks.size() == 2
req.start()
reply.start()
(0..1).each{ channels[it].subscribe(reply, forks[it]) }
think()
}
String toString() {
switch (timesEaten) {
case 0: return "$name has starved"
case 1: return "$name has eaten once"
default: return "$name has eaten $timesEaten times"
}
}
102. …Jetlang Philosophers…
GPars - 102
…
void think() {
println(name + ' is thinking')
sleep random.nextInt(3000)
(0..1).each{ AsyncRequest.withOneReply(req, channels[it], new Take(it), this); }
}
void eat() {
timesEaten++
println toString()
sleep random.nextInt(2000)
}
void onMessage(Object message) {
responses << message
if (responses.size() == 2) {
if (responses.every(gotFork)) {
eat()
}
responses.findAll(gotFork).each {
int index = it.index
channels[index].publish(req, new Release(index), forks[index])
}
responses = []
think()
}
}
}
@Immutable class Take { int index }
@Immutable class Accepted { int index }
@Immutable class Rejected { int index }
@Immutable class Release { int index }
…
103. …Jetlang Philosophers
GPars - 103
…
class Fork implements Callback {
String name
def holder = []
void onMessage(message) {
def msg = message instanceof Request ? message.request : message
def index = msg.index
switch (msg) {
case Take:
if (!holder) {
holder << index
message.reply(new Accepted(index))
} else message.reply(new Rejected(index))
break
case Release:
assert holder == [index]
holder = []
break
default: throw new IllegalStateException("Cannot process the message: $message")
}
}
}
def forks = (1..names.size()).collect { new Fork(name: "Fork $it") }
def philosophers = (1..names.size()).collect {
new Philosopher(name: names[it - 1], forks: [forks[it - 1], forks[it % names.size()]])
}
philosophers*.start()
sleep 10000
philosophers.each { println it }
104. Gruple Philosophers…
GPars - 104
import org.gruple.SpaceService
import org.gruple.Space
class Philosopher {
private random = new Random()
String name
Space space
private timesEaten = 0
int id, num
boolean done = false
void run() {
while (true) {
think()
if (done) return
space.take(fork: id)
space.take(fork: (id + 1) % num)
eat()
space.put(fork: id)
space.put(fork: (id + 1) % num)
}
}
void think() {
println "$name is thinking"
sleep random.nextInt(500)
}
void eat() {
println "$name is EATING"
timesEaten++
sleep random.nextInt(1000)
}
…
…
socrates is thinking
nietzsche is thinking
descartes is EATING
aristotle is EATING
descartes is thinking
plato is EATING
aristotle is thinking
socrates is EATING
plato is thinking
nietzsche is EATING
socrates is thinking
nietzsche is thinking
descartes is EATING
descartes is thinking
socrates has eaten 5 times
plato has eaten 4 times
aristotle has eaten 4 times
descartes has eaten 4 times
nietzsche has eaten 5 times