3. "Andy giveth and Bill taketh away"
GPars - 3
Source:HerbSutter:http://www.gotw.ca/publications/concurrency-ddj.htm
4. 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 - 4
5. Java Concurrency Best Practice?
• Java Concurrency in Practice:
–“If multiple 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
12. Immutable Classes
• Some Rules
– Don’t provide mutators
– Ensure that no methods can
be overridden
• Easiest to make the class final
• Or use static factories & non-public
constructors
– Make all fields final
– Make all fields private
• Avoid even public immutable constants
– Ensure exclusive access to any mutable components
• Don’t leak internal references
• Defensive copying in and out
– Optionally provide equals and hashCode methods
– Optionally provide toString method
69. 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 - 69
70. 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 - 70
74. Lightweight threads: Jetlang
• Jetlang
– A high performance threading library
– http://code.google.com/p/jetlang/
GPars - 74
import org.jetlang.fibers.ThreadFiber
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()) }
AsyncRequest.withOneReply(req, channel, [3, 4, 5]) { println it }
sleep 1000
req.dispose()
reply.dispose()
12
75. Other High-Level Libraries: JPPF
– Open source Grid Computing platform
– http://www.jppf.org/
GPars - 75
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
}
76. 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 - 76
82. …Other High-Level Libraries: GridGain
• http://gridgain.blogspot.com/2010/10/worlds-shortest-mapreduce-
app.html
GPars - 82
words = "Counting Letters In This Phrase".split(' ')
map = new C1() { def apply(word) { word.size() } }
reduce = sumIntReducer()
println grid.forkjoin(SPREAD, yield(words, map), reduce)
// => 27
grid.forkjoin(SPREAD,yield("Counting Letters In This Phrase".split(' '),
new C1(){def apply(w){w.size()}}),sumReducer())
83. 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 - 83
98. …Multiverse Philosophers
GPars - 98
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"
}
}
}
99. Jetlang Philosophers…
GPars - 99
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()
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"
}
}
100. …Jetlang Philosophers…
GPars - 100
…
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 }
…
101. …Jetlang Philosophers
GPars - 101
…
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 }
102. Gruple Philosophers…
GPars - 102
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