Monday, October 20, 2008

more research, synchronized wait and notify

Well, I thought that I understood how to write MIDlets, until I started putting together the pieces of talkLock. Turns out that I have more research to do.

The problem is that in a Mobile Java program, or MIDlet, you don't really have a main(). You get a startApp() method, but it really only gets run once. And your constructor, which in talkLock is named talkLock() (and in a MIDlet called Test it would have to be named test()), of course only gets run to start up your program. So the only part of the code that can get run over and over again, and do things that you would normally do in your main(), are the Commands. But Commands require user input on their menu button or keyboard or touchscreen. So what do you do with code that does something that uses a lot of cpu time, like recording audio and http POSTing it to a server?

The answer is you put that code in a separate thread. But how do you tell that thread to do it's job over and over again, if a condition is met? How do you tell it when it's time to do it's work again? It looks like this is handled by synchronize, wait, notify.

The sample code and articles I have been reading on synchronize, wait, and notify explain how to do this in a MIDlet. But what does it mean to synchronize? What does notify really do? These are java-isms.

So I am having to learn another new paradigm, this synchronize, wait, notify architecture. It's pretty limited really, I will have to write some test code to really analyze the behavior.

Here is what I understand so far: That objects are synchronized. The "synchronized" keyword means that any objects in my code that define as "synchronized" share a spinlock or mutex. Or in MVC terminology (another paradigm that I am learning to apply to this MIDlet model), these objects are Monitored.

So these objects share a spinlock. In the code for these objects, I have them check the state of some global or globals. If they are supposed to be doing something, as defined in this global or globals, then they call wait(). This tells them that they need to hear from the other objects that they share a spinlock with that it is time for them to try to grab the lock and do their work. After the wait() call, they need to check globals again, and do their work.

So if these objects are in separate threads, they can just hang out in memory, and wait for their globals to be set appropriately, and then run off and do some cpu-intensive job. The same behavior as if they were in a main(), but split off into separate threads, and using the synchronized, wait, notify concept.

How do they know it's time for them to do their work? When a notify() is sent (if you have more than two objects on the spinlock, you notifyAll()), then the synchronized objects check their globals. So if you carefully sequence events so that only in certain situations will the objects be triggered to run, then whenever a notifyAll() happens, you know what state the code will be in, and what event will be triggered, by assigning a new value to a global before you notifyAll().

By working this way you can force your code to act like it would in a normal program with a main(). I guess the idea is that your MIDlet will always be responsive to user input, and your mutexed objects will run in separate threads to make sure that main program loop (really just the system listening for user input) will always be available.

At least that's the picture I've painted for myself so far. I think I need some time to absorb these concepts, and maybe depress a little before I write my first test code. I can see a lot of frustrating debugging to get this model right. It's very difficult for me to move away from the 1980s batch programming mindset. As you might have noticed, I still look at this event-driven model from a mindset where I'm trying to make it act like a batch program with a big state machine in it. Old dogs are slow to learn new tricks, if they do.

I can understand why this model of programming is popular, you get a lot of your gui handling stuff for free this way. But I don't think that every problem is best solved with a MVC model.

Actually I think that this will let me come up with a completely new design for talkLock architecturally. Maybe I'll just put all of my code into one synchronized object that runs in a separate thread, and that code will be a big state machine :) It will be my main(), and will just be operated on by the CommandListener setting certain globals and pinging with notify(). Then I can learn this new paradigm and use it without really changing the way I write that much :)

I'll do more research, and read some source code: maybe that's really how people use it anyway :)

No comments: