41. Parallelize your arrays with JSR 166y
//Create a pool with size close to the number of processor cores
def pool = new ForkJoinPool(2)
def createParallelArray(pool, collection) {
return ParallelArray.createFromCopy(
collection.toArray(new Object[collection.size()]), pool)
}
// Enhance ArrayLists to find matching objects in parallel
ArrayList.metaClass.findAll = {Closure cl ->
createParallelArray(pool, delegate).
withFilter({cl(it)} as Predicate).all().asList()
}
def sites=['http://www.jroller.com',
'http://www.infoq.com',
'http://java.dzone.com']
def groovySites = sites.findAll {
new URL(it).text.toLowerCase().contains('groovy')}
println "These sites talk about Groovy today: ${groovySites}"
Source: http://www.jroller.com/vaclav/date/20080923 Concurrency - 41
42. 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”
Concurrency - 42
56. Lightweight threads: Jetlang
• Jetlang
– A high performance java threading library
Fiber receiver = new ThreadFiber(); // JAVA
receiver.start();
// create java.util.concurrent.CountDownLatch to notify when message arrives
final CountDownLatch latch = new CountDownLatch(1);
// create channel to message between threads
Channel<String> channel = new MemoryChannel<String>();
Callback<String> onMsg = new Callback<String>() {
public void onMessage(String message) {
//open latch
latch.countDown();
}
};
//add subscription for message on receiver thread
channel.subscribe(receiver, onMsg);
//publish message to receive thread. the publish method is thread safe.
channel.publish("Hello");
//wait for receiving thread to receive message
latch.await(10, TimeUnit.SECONDS);
//shutdown thread
receiver.dispose();
Concurrency - 56
57. Other High-Level Libraries: JPPF
– Open source Grid Computing platform
– http://www.jppf.org/
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
}
Concurrency - 57
58. Other High-Level Libraries: Gruple...
– http://code.google.com/p/gruple
– Gruple aims to provide a simple abstraction to allow
programmers 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
Concurrency - 58
59. Other High-Level Libraries: ...Gruple...
– Mandelbrot example (included in Gruple download)
...
Space space = SpaceService.getSpace("mandelbrot")
Map template = createTaskTemplate()
Map task
String threadName = Thread.currentThread().name
while(true) {
ArrayList points
task = space.take(template)
println "Worker $threadName got task ${task['start']} for job ${task['jobId']
points = calculateMandelbrot(task)
Map result = createResult(task['jobId'], task['start'], points)
println "Worker $threadName writing result for task ${result['start']} for jo
space.put(result)
}
...
Concurrency - 59
65. …Multiverse STM
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()
}
}
Concurrency - 65
66. 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
Concurrency - 66
68. GContracts
@Grab('org.gcontracts:gcontracts:1.0.2')
import org.gcontracts.annotations.*
1.8+
@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('.')
Concurrency - 68
69. Testing: Spock
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
}
}
Concurrency - 69
72. Fibonacci…
START = 8 Serial
END = 16 version
fib = {n -> n < 2 ? n : fib(n - 1) + fib(n - 2) }
(START..END).each {num ->
println "n:$num => ${fib(num)}"
}
Concurrency - 72
73. …Fibonacci…
import java.util.concurrent.*
ConcurrentHashMap
THREADS = 4 version
START = 8
END = 16
QUIT = -1
class Fibonacci {
def values = new ConcurrentHashMap()
int calc(x) { x < 2 ? x : calc(x-1) + calc(x-2) }
int calcWithCache(x) {
def result = values[x]
if (!result) {
result = calc(x)
values.putIfAbsent(x, result)
}
result
}
}
println "Calculating Fibonacci sequence in parallel..."
def queue = new ArrayBlockingQueue(10)
Concurrency - 73
74. …Fibonacci…
Thread.start('Producer') {
int x = START
while (x <= END) {
sleep 200
queue << x++
}
sleep 1000
THREADS.times { queue << QUIT }
}
(1..THREADS).each {
def name = "Consumer$it"
Thread.start(name) {
def done = false
def fib = new Fibonacci()
while (!done) {
def n = queue.take()
if (n == QUIT) done = true
else println "$name n:$n => ${fib.calcWithCache(n)}"
}
}
}
Concurrency - 74
75. …Fibonacci…
Executor
import java.util.concurrent.* version
CUTOFF = 12 // not worth parallelizing for small n
THREADS = 100
println "Calculating Fibonacci sequence in parallel..."
serialFib = {n -> (n < 2) ? n : serialFib(n - 1) + serialFib(n - 2) }
pool = Executors.newFixedThreadPool(THREADS)
defer = {c -> pool.submit(c as Callable) }
fib = {n ->
if (n < CUTOFF) return serialFib(n)
def left = defer { fib(n - 1) }
def right = defer { fib(n - 2) }
left.get() + right.get()
}
(8..16).each {n -> println "n=$n => ${fib(n)}" }
pool.shutdown()
Concurrency - 75
76. …Fibonacci…
import EDU.oswego.cs.dl.util.concurrent.FJTask
import EDU.oswego.cs.dl.util.concurrent.FJTaskRunnerGroup
class Fib extends FJTask {
static final CUTOFF = 12
Fork/Join
volatile int number version
int getAnswer() {
if (!isDone()) throw new IllegalStateException()
number
}
void run() {
int n = number
if (n <= CUTOFF)
number = seqFib(n)
else {
def f1 = new Fib(number: n - 1)
def f2 = new Fib(number: n - 2)
coInvoke(f1, f2)
number = f1.number + f2.number
}
}
int seqFib(int n) { n < 2 ? n : seqFib(n - 1) + seqFib(n - 2) }
}
Concurrency - 76
77. …Fibonacci…
def THREADS = 2
def group = new FJTaskRunnerGroup(THREADS)
def START = 8
def END = 16
(START..END).each {num ->
def f = new Fib(number: num)
group.invoke(f)
println "n:$num => $f.answer"
}
Concurrency - 77
78. …Fibonacci…
import fj.*
import fj.control.parallel.Strategy FunctionalJava
import static fj.Function.curry as fcurry version
import static fj.P1.curry as pcurry
import static fj.P1.fmap
import static fj.control.parallel.Actor.actor
import static fj.control.parallel.Promise.*
import static fj.data.List.range
import static java.util.concurrent.Executors.*
CUTOFF = 12 // not worth parallelizing for small n
START = 8
END = 16
THREADS = 4
pool = newFixedThreadPool(THREADS)
su = Strategy.executorStrategy(pool)
spi = Strategy.executorStrategy(pool)
add = fcurry({a, b -> a + b } as F2)
nums = range(START, END + 1)
println "Calculating Fibonacci sequence in parallel..."
Concurrency - 78
79. …Fibonacci…
serialFib = {n -> n < 2 ? n : serialFib(n - 1) + serialFib(n - 2) }
print = {results ->
def n = START
results.each { println "n=${n++} => $it" }
pool.shutdown()
} as Effect
calc = {n ->
n < CUTOFF ?
promise(su, P.p(serialFib(n))) :
calc.f(n - 1).bind(join(su, pcurry(calc).f(n - 2)), add)
} as F
out = actor(su, print)
join(su, fmap(sequence(su)).f(spi.parMapList(calc).f(nums))).to(out)
Concurrency - 79