Training Index Writing Advanced Applications Chapter 8 Continued: Improving Performance By Design
[<<BACK] [CONTENTS] [NEXT>>]
Bandwidth restrictions imposed on networks around the world make network-based operations potential bottlenecks that can have a significant impact on an application's performance. Many network-based applications are designed to use connection pools so they can reuse existing network connections and save on the time and overhead invested in opening and closing network connections.
Besides connection pooling, there are other features you can design into your programs to improve performance. This chapter explains how you can design an applet to download files and resources more efficiently, or design a thread-based program to use thread pooling to save on the expensive thread startup process.
Improving Applet Download Speed
Thread Pooling
Improving Applet Download Speed
Applet download performance refers to the time it takes for the browser to download all the files and resources it needs to start the applet. An important factor affecting any applet's download performance is the number of times it has to request data from the server. You can reduce the number of requests by packaging the applet images into one class file, or using JavaTM ARchive (JAR) files.
Packaging Images into One Class
Normally, if an applet has six image buttons, that translates to six additional requests sent back to the web server to load those image files. Six additional requests might not seem like much on an internal network, but given connections of lesser speed and reliability, those additional requests can have a significant negative impact on performance. So, your ultimate goal should be to load the applet as quickly as possible.
One way to store images in a class file is to use an ASCII encoding scheme such as X-PixMap (XPM). This way, rather than maintaining the images as GIF files on the server, the files are encoded as Strings and stored in a single class file.
This code sample uses packages from the JavaCup winner at JavaOne 1996, which contains the XImageSource and XpmParser classes. These classes provide all you need to read a standard XPM file. You can see these files at SunSite.
For the initial encoding process, there are a number of graphics tools you can use to create XPM files. On Solaris you can use ImageTool or a variety of other GNU image packages. Go to the Download.com web site to get the encoding software for Windows platforms.
The following code excerpted from the MyApplet sample class loads the images. You can see the coded String form for the images in the XPM definition of the images.
The Toolkit class creates an Image object for each image from the XPM Image Source object.
Toolkit kit = Toolkit.getDefaultToolkit(); Image image; image = kit.createImage (new XImageSource (_reply)); image = kit.createImage (new XImageSource (_post)); image = kit.createImage (new XImageSource (_reload)); image = kit.createImage (new XImageSource (_catchup)); image = kit.createImage (new XImageSource (_back10)); image = kit.createImage (new XImageSource (_reset)); image = kit.createImage (new XImageSource (_faq));
The alternative technique below uses GIF files. It requires a request back to the web server for each image loaded.
This technique reduces network traffic because all images are available in a single class file.
Using XPM encoded images makes the class size larger, but the number of network requests fewer.
Making the XPM image definitions part of your applet class file, makes the image loading process part of the regular loading of the applet class file with no extra classes.
Once loaded, you can use the images to create buttons or other user interface components. This next code segment shows how to use the images with the javax.swing.JButton class.
ImageIcon icon = new ImageIcon ( kit.createImage ( new XImageSource (_reply))); JButton button = new JButton (icon, "Reply");
Using JAR Files
When an applet consists of more than one file, you can improve download performance with Java ARchive (JAR) files. A JAR file contains all of an applet's related files in one single file for a faster download. Much of the time saved comes from reducing the number of HTTP connections the browser must make.
Chapter 9: Deploying Your Application has information on creating and signing JAR files.
The HTML code below uses the CODE tag to specify the executable for the MyApplet applet, and the ARCHIVE tag to specify the JAR file that contains all of MyApplet's related files. The executable specified by the CODE tag is sometimes called the code base.
For security reasons the JAR files listed by the archive parameter must be in the same directory or a sub-directory as the applets codebase. If no codebase parameter is supplied the directory from where the applet was loaded is used as the codebase.
The following example specifies jarfile as the JAR file that contains the related files for the MyApplet.class executable.
If the applet download uses multiple JAR files as shown in the next HTML segment, the ClassLoader loads each JAR file when the applet starts. So, if your applet uses some resource files infrequently, the JAR file containing those infrequently used files is downloaded, regardless of whether the resources are actually used during that session or not.
To improve performance when an applet has infrequently used files, put the frequently used files into the JAR file and the infrequently used files into the applet class directory. Infrequently used files are then located and downloaded by the browser only when needed.
Thread Pooling
The Java Developer ConnectionSM (JDC) applet servers and the Java Web ServerTM make extensive use of thread pooling to improve performance. Thread pooling is creating a ready supply of sleeping threads at the beginning of execution. Because the thread startup process is expensive in terms of system resources, thread pooling makes the startup process a little slower, but improves runtime performance because sleeping (or suspended) threads are awakened only when they are needed to perform new tasks.
This code sample taken from the Pool.java class shows one way to implement thread pooling. In the pool's constructor (shown below), the WorkerThreads are initialized and started. The call to the start method executes the run method of the WorkerThread, and the call to wait in the run method suspends the Thread while the Thread waits for work to arrive. The last line of the constructor pushes the sleeping Thread onto the stack.
public Pool (int max, Class workerClass) throws Exception {
_max = max; _waiting = new Stack(); _workerClass = workerClass; Worker worker; WorkerThread w; for ( int i = 0; i < _max; i++ ) { worker = (Worker)_workerClass.newInstance(); w = new WorkerThread ("Worker#"+i, worker); w.start(); _waiting.push (w); } }
Besides the run method, the WorkerThread class has a wake method. When work comes in, the wake method is called, which assigns the data and notifies the sleeping WorkerThread (the one initialized by the Pool) to resume running. The wake method's call to notify causes the blocked WorkerThread to fall out of its wait state, and the run method of the HttpServerWorker class is executed. Once the work is done, the WorkerThread is either put back onto the Stack (assuming the Thread Pool is not full) or terminates.
synchronized public void run(){ boolean stop = false; while (!stop){ if ( _data == null ){ try{ wait(); }catch (InterruptedException e){ e.printStackTrace(); continue; } }
if ( _data != null ){ _worker.run(_data); }
_data = null; stop = !(_push (this)); } }
At its highest level, incoming work is handled by the performWork method in the Pool class (shown below). As work comes in, an existing WorkerThread is popped off of the Stack (or a new one is created if the Pool is empty). The sleeping WorkerThread is then activated by a call to its wake method.
public void performWork (Object data) throws InstantiationException{ WorkerThread w = null; synchronized (_waiting){ if ( _waiting.empty() ){ try{ w = new WorkerThread ("additional worker", (Worker)_workerClass.newInstance()); w.start(); }catch (Exception e){ throw new InstantiationException ( "Problem creating instance of Worker.class: " + e.getMessage()); } }else{ w = (WorkerThread)_waiting.pop(); } } w.wake (data); }
The HttpServer.java class constructor creates a new Pool instance to service HttpServerWorker instances. HttpServerWorker instances are created and stored as part of the WorkerThread data. When a WorkerThread is activated by a call to its wake method, the HttpServerWorker instance is invoked by way of its run method.
try{ _pool = new Pool (poolSize, HttpServerWorker.class); }catch (Exception e){ e.printStackTrace(); throw new InternalError (e.getMessage()); }
This next code is in the run method of the HttpServer.java class. Every time a request comes in, the data is initialized and the Thread starts work.
Note: If creating a new Hashtable for each WorkerThread presents too much overhead, just modify the code so it does not use the Worker abstraction.
try{ Socket s = _serverSocket.accept(); Hashtable data = new Hashtable(); data.put ("Socket", s); data.put ("HttpServer", this); _pool.performWork (data); }catch (Exception e){ e.printStackTrace(); }
Thread pooling is an effective performance-tuning technique that puts the expensive thread startup process at the startup of an application. This way, the negative impact on performance occurs once at program startup where it is least likely to be noticed.
[TOP]
[ This page was updated: 13-Oct-99 ]
Products & APIs | Developer Connection | Docs & Training | Online Support Community Discussion | Industry News | Solutions Marketplace | Case Studies
Glossary - Applets - Tutorial - Employment - Business & Licensing - Java Store - Java in the Real World
FAQ | Feedback | Map | A-Z Index
For more information on Java technology and other software from Sun Microsystems, call:
(800) 786-7638
Outside the U.S. and Canada, dial your country's AT&T Direct Access Number first.