CyclicBarriers
. Here's one usage that wouldn't go into production, but can be illuminating.Background
The Hibernate book has 2 large sections: one on structure; the other, on behaviour. It is easy to come up with a good, isolated example for explorations of mapping structure (e.g. collections). It's not as easy to isolate behaviour with respect to isolation levels and concurrency (e.g. simultaneous inserts to the DB).
However, with the threading tools, it is definitely possible (and fun) to create some homegrown chaos in isolation. Consider this scenario:
- Several threads each open a transaction against the database
- Each thread creates a new object with a common value in a field designated as unique in the DB
- Each thread then attempts to commit its record into the DB: chaos!
CyclicBarrier
However, with the CyclicBarrier, we can precisely hold up the threads until the Moment of Maximum Pathogenesis. In this case, after the creation but before the commit.
It is easy to use Alex's driver program to execute several of worker threads. The gist is as follows:
- Initialize Hibernate's
SessionFactory
- Create a
ThreadPoolExecutor
- Create a
CyclicBarrier
of size N - Create N workers (see below): each one receives a handle to the
SessionFactory
andCyclicBarrier
- Pass the workers into the executor
Owner
POJO (from a car example). In the DB, the Owner
table simply has a surrogate key and a my_name
field which has a unique constraint.The individual worker looks like this:
class MyTask implements Runnable {
private int taskId;
private CyclicBarrier barrier;
private SessionFactory sessionFactory;
// omitting ctor and emit() method
// for brevity
public void run() {
try {
emit("waiting to start");
// begin Txn
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
// create new persistent object
Owner owner = new Owner();
owner.setName("CodeToJoy");
session.saveOrUpdate(owner);
// barrier: wait for all threads
emit("waiting...");
barrier.await();
// unleash the hounds!
tx.commit();
session.close();
emit("done");
} catch(Exception ex) {
emit("caught : " + ex.toString() );
}
}
}
Here is example output (cleaned-up considerably):
starting....
task 1 : waiting to start
task 2 : waiting to start
task 3 : waiting to start
task 4 : waiting to start
task 5 : waiting to start
task 4 : waiting...
task 3 : waiting...
task 5 : waiting...
task 1 : waiting...
task 2 : waiting...
barrier reached (NOTE: from a println the CyclicBarrier itself)
(NOTE: thread ids may not match primary key)
task 5 : done
task 1 : insert into Owner (my_name, id)
values (CodeToJoy, 1) was aborted.
task 3 : insert into Owner (my_name, id)
values (CodeToJoy, 5) was aborted.
task 4 : insert into Owner (my_name, id)
values (CodeToJoy, 4) was aborted.
task 2 : insert into Owner (my_name, id)
values (CodeToJoy, 2) was aborted.
(NOTE: list all records in the DB. Task 5 won and received id = 3)
owner id = 3
ownername = CodeToJoy
The Upshot
In this way, the threading utilities can be used to come up with many weird and wonderful situations. Implementing these ideas can illuminate the concepts in the Hibernate book (and in the Java concurrency libraries).
nice!
ReplyDelete