You are on page 1of 9

Threading in J2ME (MIDP2.

0)
Introduction
Threads allow a MIDlet to execute more than one task simultaneously. However, it should be noted that as current Motorola handsets only have one CPU, true parallel processing cannot be achieved. Multiple threads will use CPU time-slicing (also known as thread scheduling) which gives the impression of parallel instruction execution. Threads are particularly useful to maintain the MIDlet User Interface responsiveness and functionality while other lengthy sections of code are processed. Without using threads the User Interface of a complex MIDlet can become “blocked” until a suitable time when the system can “repaint” the display, potentially presenting long blank periods in the User Interface to the user. Every MIDlet has at least one thread, a MIDlet with more than one thread is known as a multi-threaded MIDlet.

Thread Usage and Methods
Every thread has a “context”. The context provides information about the thread, and is updated during thread execution. This “context” contains thread variable contents, thread execution address information and the thread state. A thread can be in a number of different “states” as follows: • Ready (Thread is ready to execute) • Running (Thread is executing) • Suspended (Thread is waiting for external event to occur such as data delivery) • Terminated (Thread has finished) The methods available within the Thread class are as follows:
static int

activeCount()

Returns the current number of active threads in the VM.
static

Thread currentThread()

Returns a reference to the currently executing thread object.
int

getPriority()

Returns this thread's priority.
boolean

isAlive()

Tests to see if this thread is alive.
void

join()

Waits for this thread to die.

including a unique number that identifies the thread and the thread's priority. . String toString() Returns a string representation of this thread. the startApp() mthod is called resulting in any code in the startApp() method being executed. static void millis) Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds. static void yield() Causes the currently executing thread object to temporarily pause and allow other threads to execute. otherwise. Each time a MIDlet is suspended the pauseApp() method is called. Thread Blocking and Dead-Locking Thread synchronization is very important as without it Threads can work against one another and easily become “blocked”. void start() Causes this thread to begin execution. this method does nothing and returns.void run() If this thread was constructed using a separate Runnable run object. one will have to wait until the other has finished the synchronized block. Synchronising Threads. void setPriority(int sleep(long newPriority) Changes the priority of this thread. If threads are created in this method multiple suspend resumes of the MIDlet could lead to multiple instances of the same thread being created/started thus leading to very slow MIDlet execution or a shortage of Heap memory proportional to the number of suspend/resumes. Threading and MIDlet lifecycle Extreme care must be exercised when creating threads in the startApp() lifecycle method. This can occur if two threads try to access the same “shared” data at the same time. If this occurs one thread will have to wait until the other relinquishes the hold on the object data. thus it will be in a blocked state. the Java Virtual Machine calls the run method of this thread. then on a MIDlet Resume. } // synchronized block finish } If two threads attempt to call this method at the same time.append( "Hello World" ). then that Runnable object's run method is called. Example code: public void buildMessage(StringBuffer strBuffer){ // synchronized block start synchronized(strBuffer){ strBuffer.

The same example in this other style looks like the following: class PrimeRun implements Runnable { long minPrime. . The two threads become blocked . An instance of the class can then be allocated. i. and started. One is to declare a class to be a subclass of Thread. a thread that computes primes larger than a stated value could be written as follows: class PrimeThread extends Thread { long minPrime. An instance of the subclass can then be allocated and started. p. Threading example code snippets Every thread has a priority. passed as an argument when creating Thread. There are two ways to create a new thread of execution. . consider the following situation: 1.minPrime = minPrime. } public void run() { // compute primes larger than minPrime . } } The following code would then create a thread and start it running: PrimeThread p = new PrimeThread(143).this is known as a Deadlock and must be avoided in the MIDlet code design. The other way to create a thread is to declare a class that implements the Runnable interface. .e. PrimeThread(long minPrime) { this. Threads with higher priority are executed in preference to threads with lower priority. } public void run() { // compute primes larger than minPrime . That class then implements the run method. This subclass should override the run method of class Thread. . . Thread_1 attempts to lock Object_B 4. PrimeRun(long minPrime) { this.start(). Thread_2 locks Object_B for access 3. For example.minPrime = minPrime. Simultaneously Thread_2 attempts to lock Object_A. Thread_1 locks Object_A for access 2.Two threads can become “deadlocked” if each thread attempts to access (lock) object data already being accessed (locked) by the other thread.

new Thread(p). remember to close the socket.java * * Created on 18 March 2005. the number of available connections (four) will run out and all following opens will fail.io. SSL. session. /** * An example MIDlet which demonstrates the use of multi-threading * with an HTTP connection in one thread and the MIDlet in the main * thread.start(). input/output streams.io.*. they should always be performed in their own thread additional to the main MIDlet thread. // HTML raw data Buffer size byte databuf[] = new byte[ BUFSIZE ].microedition. // HTTP raw data buffer Display display.lcdui.} } The following code would then create a thread and start it running: PrimeRun p = new PrimeRun(143). The read/write block operations are more efficient than read/write byte.*. 3. // The display object Form form.microedition. 15:18 */ package HTTP.0 */ public class HTTP extends MIDlet implements CommandListener { private private private private private final int BUFSIZE = 8192. But for high data rate it is recommended on two threads. each thread is recommended on one thread. import import import import javax. Check for validity of the socket.midlet. If the MIDlet opens two or more sessions concurrently. * * @author Motocoder * @version 1. javax.*. javax. Threading and Networking When performing networking operations. // The HTTP thread object . then if the socket is not null. If the MIDlet forgets to close. socket and datagram: the write and read can be on the same thread.*. Always handle the IOException. Here are some general recommendation when networking with threads: 1. Use readFully() with caution since the readfully() will not return if the buffer is not filled up fully or the server is closing the connection. 2. HTTP/HTTPS all open: input/output of one session is recommended to be in one thread due to the sequential nature of HTTP sessions. An example of Multi-threading in conjunction with Networking HTTP is below: /* * HTTP.microedition. // The Form UI object HttpThread hthread. java.

len = (int)conn. form. form.motocoder. 1. exitCmd = new Command("Exit". // The URLs to be accessed public HTTP() { // initialise objects form = new Form("HTTPThread").setCommandListener(this). "http://www. int len = 0.com".getDisplay(this).openInputStream(). // The Exit Command object private String urls[] = { "http://www. conn = (HttpConnection)Connector.motorola. } public void startApp() { } public void pauseApp() { } public void destroyApp(boolean unconditional) { hthread.start().append("Opening inputstream\n").read( databuf. 0.killThread(). if(len == -1) { readlen = is. } private void getUrl(String url) { HttpConnection conn = null. // The URL count pointer private Command exitCmd.getResponseCode().setCurrent(form).getLength().open(url). // Initialise and start the HTTP reader thread hthread = new HttpThread(). try { bos = new ByteArrayOutputStream(). form. ByteArrayOutputStream bos = null. notifyDestroyed(). form. public HttpThread() { running = true.com" }.addCommand(exitCmd). form. hthread. } public void commandAction(Command c.private int urlcnt. } } // The HTTP reader thread inner class class HttpThread extends Thread { private boolean running. . is = conn. int readlen = 0. // set the display display. int rescode = 0. Command.append("Connecting to " + url +"\n").append("responsecode: " + rescode + "\n").append("Reading data " + len + " bytes\n").append("Reading responsecode\n"). Displayable d) { if(c == exitCmd) { destroyApp(true). urlcnt = 0. rescode = conn.EXIT). form. form. display = Display. InputStream is = null. BUFSIZE ).

length) { urlcnt = 0. 0. form. readlen ).read( databuf. 0. } finally { form.append("Error closing connection\n" ).read( databuf.write( databuf. break //while loop and signal to application of the //error statement // Don't forget to close the streams and //connections } else { totread += readlen.toString() + "\n"). } catch(Throwable t) { . } public void run() { while(running) { try { getUrl(urls[urlcnt]). int totread = readlen.read( databuf. readlen ). 0. } } } } catch(IOException ioe) { System. } catch(IOException ioe) { form. } catch(IOException ioe) { form.write( databuf. if( readlen == -1 ) { //An error has occurred while reading. form.append(ioe. } } else { readlen = is. readlen = is.while( readlen != -1 ) { bos. while( totread != len ) { bos.toString()). BUFSIZE ). readlen = is.println(ioe.append("Error closing inputstream\n" ).toString() + "\n"). BUFSIZE ). 0. 0.close(). } } if(conn != null) { try { conn.append(t. } } } } public void killThread() { running = false. } try { sleep(10000).close().out. if(is != null) { try { is.append("Closing connection\n").append(ioe. if(urlcnt == urls.toString() + "\n"). urlcnt++. } } catch(Throwable t) { form. BUFSIZE ).

java. int playState = 0.midlet.addCommand(exitCmd). Form form.*. // Set the UI display display. // // // // // // // // // // // The The The The The The The The The The The Display object Form UI object Play Command object Exit Command object thread running flag music playing flag Player object music file to play media format playing status of the player thread which controls the Player public MediaPlayerSample() { // Initialise MIDlet/UI objects display = Display.*. exitCmd = new Command("Exit". } } } } } Threading with the MME An example of Multi-threading in conjunction with the Multi-Media Engine is below: /* * MediaPlayerSample. 1).microedition.SCREEN. form.microedition. // Create one instance of the Play Thread ptr = new PlayThread(). String fileType = "audio/mid". Command exitCmd. javax. PlayThread ptr.lcdui. form.*.toString() + "\n").java * * Created on 18 March 2005.setCurrent(form). * * @author Motocoder * @version 1. Command playCmd.addCommand(playCmd).media.mid". form = new Form("MediaPlayerSample"). // Add UI objects to UI and set softkey listener form.form. 15:18 */ package MediaPlayerSample.*.append(t. Player musicPlayer. threadRunning = true.setCommandListener(this).0 */ public class MediaPlayerSample extends MIDlet implements CommandListener { private private private private private private private private private private private Display display. String soundFile = "myfile. import import import import import javax. boolean musicPlaying = false. javax.microedition. /** * An example MIDlet which demonstrates the use of multi-threading * with a Player in one thread and the MIDlet in the main thread. 2). Command.start(). javax.io. boolean threadRunning = false. Command. ptr.getDisplay(this).io.microedition. } protected void startApp() { . playCmd = new Command("Play1".EXIT.*.

fileType = "audio/mid".playSound().join() as long as flag is set to false before join() is called switch(playState) { case 0: break. } catch (MediaException me) { form. playState = 0.toString()). } public void commandAction( Command c. } // Detect Exit softkey press if(c == exitCmd) { destroyApp(true)..append(me.1 Thread. playState = 0. musicPlaying = false. notifyDestroyed(). musicPlaying = false. } } break.mid". musicPlaying = false. //Global flag to keep thread running until we want // (would allow for CLDC 1. ptr. musicPlayer = null. if (musicPlayer != null) { musicPlayer.start(). // Be nice to the system for a while } catch(Throwable t) { } } . } } // The Thread that runs the Player class PlayThread extends Thread implements PlayerListener { public void run() { while(threadRunning) { to exit. } protected void destroyApp(boolean unconditional) { threadRunning = false. musicPlaying = true. try { musicPlayer.// Initialise everything as MME will be stopped when // returning from suspend/resume. case 1: if (!musicPlaying) { musicPlayer = makePlayer().deallocate(). Displayable d ) { // Detect Play softkey press if(c == playCmd) { soundFile = "hazard. } } try { sleep(50). } } protected void pauseApp() { playState = 0.

} else { form. } } return player. fileType ).1 is a superset of CLDC except for Thread.0 and CLDC1. player. in CLDC1. such as V980 and E1000. For Thread. form. player.addPlayerListener(this). } } // utility method to create and return a Player public Player makePlayer() { InputStream is = null. form.append("start\n"). try { form.0.append(event).join() to closer match Java2SE (compared with CLDC1.END_OF_MEDIA ) { p. //etc add necessary methods to deallocate and possibly close the Player object.3).join(). the system performs an immediate "return.1 was designed in the JCP.append("open inputstream\n"). player. Player player = null. } catch( Throwable t ) { form.append("prefetch\n"). implements the new version of CLDC: CLDC1.1 standard. When CLDC1.0. form. form.createPlayer( is. In the CLDC 1.join() method outlined below. the expert group decided to change the behavior of Thread. //allow threads to start playing a sound again. there are changes to the functionality of the Thread. is = getClass(). The result of this is that the MIDlet won't end until the thread finishes.getResourceAsStream(soundFile). } } } } Differences between CLDC1.interrupt().deallocate(). they decided to change the behavior of Thread. Object evdata ) { if( p == musicPlayer && event == PlayerListener.1 it keeps waiting unless other threads invoke Thread.join().append( t.toString() ). player = Manager.3.append("realize\n").realize()." In CLDC1.prefetch(). .// Utility method to pass message to thread to start Player public synchronized void playSound() { if( playState == 0 ) { playState = 1. playState = 0. CLDC1. String event. } // Catch and handle Player events public void playerUpdate( Player p.append("create player\n"). musicPlaying = false.deallocate().join(). if( musicPlayer != null ) { musicPlayer. Specifically.1 The KVM in Our newer handsets.1.