J2ME and Multithreading
« on: September 17, 2004, 04:40:54 am »
J2ME and multithreading mini-article
The scope of this article is to give a brief overview of how threading works in Java with a particular focus on J2ME. My reason for writing this is to address some misconceptions people may have.
Now, getting straight to it...
Java doesn't do threads.
Java itself does not offer any threading functionality. It merely abstracts the functionality found in the OS. Because of this fact, the behavior of threading in Java is said to be "non-deterministic". In other words, Java makes no guarantees as to how multithreading will be accomplished.
There is no such thing as 2 threads running at the same time on a single CPU device
Obviously in J2ME devices there is only one processor so the appearance that many threads are running simultaneously is incorrect. Only one thread is ever running at a time. This means having two threads executing 1 job each is actually slower than having one thread perform the same 2 jobs. Why? Because nothing comes for free and there is management overhead when switching threads.
About Cooperative Multitasking
Your J2ME device's OS is most probably running a simplistic cooperative multitasker. I say probably because I don't know what handset you are using but I know that if it had the power to do preemptive multitasking it would probably run J2SE and not J2ME.
What's the difference? Well, with preemptive multitasking the CPU gives a slice of time to each thread so that each gets a chance to execute. With cooperative multitasking each thread either terminates and relinquishes the CPU or has to explicitly yield control of the CPU over. So what does that mean? It means having 10 balls bouncing around, each with its own thread really makes one ball bounce around at a time since one thread is active at a time. Remember, each ball's thread continues until it dies, then the next ball gets its chance (in the worst case scenario of 100% cooperative multitasking).
So what if I yield each of my 10 threads every xxxx milliseconds? Will it make the balls bounce more smoothly?
Possibly, it depends on the OS' thread scheduler. But this much is for certain... All the work for saving thread A's context and retrieving thread B's context every xxxx milliseconds means your balls will bounce sloooooowwwwly. The CPU might even spend more time managing thread contexts than it does running the threads themselves.
The threads that are already running
Whether you know it or not your application is running its own "main" thread, all your key events are being captured on another thread, and your painting operations occur on yet another thread.
Be certain that your application needs another thread before implementing it!
When is a good time to use threads then?
Multiple threads are useful when you want to allow 2 things to occur independently. For instance, you may want to monitor and inform the progress of an operation like loading graphics from your JAR file.
When is a bad time to use threads?
Avoid thread use for animation. Conduct the animation in your main game loop. An animation is like a flip-book where each static image is displayed in sequence to provide the illusion of movement. Your main game loop is perfect for that task. It keeps refreshing the screen for you so just prepare the next image and have it displayed in sync with the next refresh.
Conversely, if you were to use another thread to control the animation you might encounter race conditions where the animation thread and the main thread (where you're game logic resides) touch the same image object. You'd have to synchronize your code to avoid these situations but synchronized code runs 4x slower than normal code so you're really just shooting yourself in the foot.
Here is a compact list of facts you should know about Threads...
- Setting the priority of a thread does not guarantee that the thread will have more or less of a priority over other threads. The priority number you assign may not even map to the OS's priority system if it has one at all.
- Calling Thread.sleep(1000) will cause the thread to sleep at least 1000 milliseconds and perhaps more. The thread scheduler takes time to bring the thread back to life and has to consider other running threads' contexts as well. The main takeaway here is to not count on sleep being an accurate time keeper if your logic requires precision. Speaking from experience, most of my Threads that sleep for 1000 milliseconds actually come back to life 1010 milliseconds later in my emulator.
- Each thread has its own call stack and that means any thread can cause the application to crash if exceptions are not properly caught in each run() method.
- Synchronized code runs 4 times slower than non-synchronized code. Avoid it unless there is a possiblity of a race condition occuring in the code.
Threads don't necessarily make your code run faster and in many cases can even slow down your code. How Threads behave varies between devices and the emulator and there is no guarantee of how Threads will be scheduled. There is an appropriate time to use Threads but in most cases they are not needed and should be avoided.