Threading, Concurrency, Synchronization
There comes a time in every developer’s career where he (or she) will need to run multiple tasks at same time and access most of the same data. When this happens the most apparent solution has been to use the concept of threading, thread locking, and simple synchronization techniques. When you’re not working within the same process one would have to work with synchronization across processes; those use things like semaphores but we won’t get into those today.
Sonic
I love Sonic. I especially love that you can wait for your order in your car and the waitress will come to give you your order when it is done. This sounds like a nice threading model, so let’s use it. There are several things about our model that we already know about:
Menu –
has a ton of choices to create a meal
Meal – a list of choices
created by someone with an order
Order – a purchase made by
someone for some meal
Station – a place for someone to
make an order and wait for their meal
Kitchen – the place that receives the orders and prepares the meals to be sent
to stations
At Sonic there are lots of stations where people can pull into to make an order. So, in SOA terms each station is a client, right? Right, so what’s our service? Well we could make it the kitchen but think about how much work the kitchen will have to do to keep up with all the work, making the food, letting the station know they are done waiting, preparing orders, and taking orders… way too much for any one kitchen to handle. So we need a Central Station to take the orders, record them and wait until meals are cooked in order to send them to each station.
From our Central Station this is what we want to effectively accomplish:
public Meal MakeOrder(Order order) {
Kitchen.StartCooking(order, this);
synchronized (this) {
this.wait();
}
return Kitchen.GetMeal(this);
}
- Let the kitchen know that this context (order and station) will be waiting for a meal
- Tell the kitchen to start making the order (should create a new thread)
- Wait until the meal is done cooking.
- Get the meal from the kitchen and send it back to the client station.
Notice how we pass the -this- object into the StartCooking method. The kitchen has to know who is actually waiting in order to notify that instance to stop waiting. This is accomplished by the following.
public void StartCooking(Order order, Object waiter) {
waiters.put(order, waiter);
Thread thread = new Thread(new CookingProcess(order));
thread.start();
}
Of course, what you’re not seeing here is the Hashtable and the cooking process but I’ll get to that in a moment. Remember that we must start a new thread here in order to return from this immediately. I have defined a Hashtable to store the waiter with its order.
private Hashtable<Order, Object> waiters = new Hashtable<Order, Object>();
When the CookingProcess is complete it will call the Kitchen’s OrderUp method passing in the order it received. That is done by the following code:
public void OrderUp(Order order) {
Object waiter = waiters.get(order);
synchronized (waiter) {
waiter.notify();
}
}
Again, notice that I am using the Hashtable from above to obtain the waiter. We call its notify method to tell it to stop waiting. This will cause the original MakeOrder context to continue from the wait to:
return Kitchen.GetMeal(this);
One thing that has popped up again and again is that synchronized code description. There are basically 2 rules of thumb in working with threaded code and objects.
1) If you access it and modify it in more than one thread. Lock it (synchronized)
2) If you need to change the state of another thread. Lock it (synchronized)
Otherwise, if you’re reading across multiple threads there should be no issue. If you happen to run into it, just remember those rules and you should be fine.
Finally, here is the code used to create the CookingProcess:
public class CookingProcess implements Runnable {
Order order;
public CookingProcess(Order order) {
this.order = order;
}
public void run() {
// get the first available chef
// cook the meal, when that's done
Kitchen.OrderUp(order);
}
}
There is nothing too special going on here; just that we must implement the Runnable interface to ensure that this is used as a thread processing unit. Finally, all we have to do is create some client stations to simulate a real-world Sonic environment.
public void ReadyToOrder() {
Order order = TakeOrderFromCustomer();
Meal meal = MakeOrder(order); // returns when done
EatMeal(meal);
}
And there you have it. This could be an actual simulation of what an SOA-based Sonic would look like. You can add a few things to make it better like the ReadyToOrder can be someone on a headset taking the order from the customer. A station can be defined to make a request for someone to take their order and wait until one is ready (hint: wait & notify).
Nevertheless, in this tutorial you learned the basic principles of synchronized code. A few techniques in thread state management and a nice SOA overview using Sonic as an example.