google search

Tuesday, March 30, 2010

NETWORKING IN JAVA

In this, we'll cover networking with Java. Don't worry if you're not familiar with networking - this will be only a brief introduction. We'll examine some of the classes in the java.net package, and show you how to write a simple network client in Java. First, however, we need to cover some background theory.

How do computers talk to each other via the Internet?

The Internet is composed of millions of computers, located all across the globe, communicating and transmitting information over a variety of computing systems, platforms, and networking equipment.  Each of these computers (unless they are connecting via an intranet) will have a unique IP address.
IP addresses are 32-bit numbers, containing four octets (8 bit numbers) separated by a full stop. Each computer with a direct internet connection will have a unique IP address, (e.g. 207.68.156.61). Some computers have temporary addresses, such as when you connect to your ISP through a modem. Others have permanent addresses, and some even have their own unique domain names .
An IP address allows us to uniquely identify a device or system connected to the Internet. If I wanted to connect to a specific IP address, and send a message, I could do so. Without an IP address, my message would have no way of reaching its destination - a bit like leaving the address off a letter or parcel.
Often, computers connected to the Internet provide services. This page is provided by a web server, for example. Because computers are capable of providing more than one type of service, we need a way to uniquely identify each service. Like an IP address, we use a number. We call this number a port. Common services (such as HTTP, FTP, Telnet, SMTP) have well known port numbers. For example, most web servers use port 80. Of course, you can use any port you like - there's no rule that says youmust use 80.
diagram
Figure 1.0 - Ports help computers identify which service data is for.
There are several communications mechanisms that we can use to provide network services. We could use UDP (unreliable datagram protocol), or TCP (transfer-control protocol). For the purposes of this tutorial, we'll choose TCP, because it makes life much easier. TCP guarantees that messages will arrive at their destination. UDP is unreliable, and your application isn't notified if the message is lost in transit. Also, many protocols (such as HTTP, SMTP, POP & FTP) use TCP, so it's important that you are familiar with it for networking in Java.

Internet Addressing with Java

Handling internet addresses (domain names, and IP addresses) is made easy with Java. Internet addresses are represented in Java by the InetAddress class. InetAddress provides simple methods to convert between domain names, and numbered addresses.
We start by importing the java.net package, which contains a set of pre-written networking routines (including InetAddress).
import java.net.*;
Next, we declare a new variable of type InetAddress, which we assign the value of the local host machine (for machines not connected to a network, this should represent 127.0.0.1). Due to the fact that InetAddresses can generate exceptions, we must place this code between a try .. catch UnknownHostException block.
// Obtain the InetAddress of the computer on which this program is running
InetAddress localaddr = InetAddress.getLocalHost();
The InetAddress class has methods that return the IP address as an array of bytes (which can be easily converted into a string), as well as a string representation of its domain name (e.g. mydomain.org ). We can print out the InternetAddress, as well as the domain name of the local address.
System.out.println ("Local IP Address : " + localaddr );
System.out.println ("Local hostname : " + localaddr.getHostName());

public class MyFirstInternetAddress
{
 public static void main(String args[])
 {
  try
  {
   InetAddress localaddr = InetAddress.getLocalHost();
   
   System.out.println ("Local IP Address : " + localaddr );
   System.out.println ("Local hostname   : " + localaddr.getHostName());
  }
  catch (UnknownHostException e)
  {
   System.err.println ("Can't detect localhost : " + e);
  }
  
 }

 /** Converts a byte_array of octets into a string */
 public static String byteToStr( byte[] byte_arr )
 {
  StringBuffer internal_buffer = new StringBuffer();

  // Keep looping, and adding octets to the IP Address
  for (int index = 0; index < byte_arr.length -1; index++)
  {
   internal_buffer.append ( String.valueOf(byte_arr[index]) + ".");
  }
  
  // Add the final octet, but no trailing '.'
  internal_buffer.append ( String.valueOf (byte_arr.length) );

  return internal_buffer.toString();
 }
}
Compile and run this application, and you should be told your local IP address, and hostname. Don't worry if your computer isn't connected to the Internet, though. Providing your system has a TCP stack, it should give you back an IP address even if you aren't currently connected. On most systems, you can refer to your local machine (which often has the hostname "localhost") as IP address 127.0.0.1
Why would every machine that's not connected to the Internet have the same address? This address is known as a loopback address. Every time you connect to this address, you're actually connected to your local machine. So, if you were running a local webserver, and you pointed your browser to, you should see your web-site. But if I were to go to the same address, I'd connect to a different site - that of my own machine.
This is great when developing Java applications. You don't need a permanent connection to the Internet - you can run client and server applications on your own machine. This is handy, because writing and testing client/server applications can take some time, and unless you have a permanant connection, you wouldn't want to be billed on an hourly rate by your ISP!

Writing a TCP client in Java

Writing network client in Java is very simple. If you've ever written a network client in C, you'll know how complicated it can be. You have to be concerned with structures, and pointers. Java cuts out this complexity, through its java.net.Socket class. To demonstrate just how easy Java makes it, I'm going to show you how to write a finger client.
For those who are unfamiliar with the finger protocol, I'll briefly explain how it works. Finger allows a remote user to query a host machine for information, either about the host machine in general or a specific user. Most unix systems support finger, and many non-Unix systems also support the protocol. Most finger applications take as a paramater 'username@hostmachine'.
Finger clients connect to a host server at port 79 and establish a TCP stream. The client sends the username (or a blank, for a general query), followed by a newline character. The server then sends back information about the user, in the form of a text stream. This should be displayed to the user, and then the connection should be terminated.
As with any networking application in Java, we need to first import the network and input/output packages.
import java.io.*;
import java.net.*;
Our application should have a single method, main, which is responsible for issuing a finger query. The first step is to validate and parse the command line paramaters, looking for a username and hostname.
public static void main ( String args[] )
{
 // Check command line paramaters
 if (args.length != 1)
 {
  System.err.println ("Invalid paramaters");
  System.exit(1);
 }
 else
 // Check for existence of @ in paramater
 if (args[0].indexOf("@") == -1)
 {
  System.err.println ("Invalid paramater : syntax user@host");
  System.exit(1);
 }

 // Split command line paramater at the @ character
 String username = args[0].substring(0, args[0].indexOf("@") );
 String hostname = args[0].substring(args[0].indexOf("@") +1, args[0].length());

 ........
}
In the code above, we check that only a single paramater has been entered, and also that there exists an '@' character in the paramater. The next step is to split the command line paramater into a username and a hostname. To do this, we rely on the substring method which can be applied to any string. Username becomes the string from offset 0, to the first index of character '@'. Hostname becomes the string from the first index of character '@', to the length of the original paramater.
Figure 2.0 - Extracting username & hostname from command-line parameter
The next step is to connect to the finger server, which opperates on port 79. As with the previous example, we must enclose our network code inside of a try { ... } catch block. This allows us to trap any network errors that may occur (such as invalid hostnames, or an inability to connect with the server). You'll notice that the code to create a TCP collection is actually only a single line - networking in Java is very easy.
try
{
 // Create a connection to server
 Socket s = new Socket(hostname, 79);

 // Remainder of finger client code goes here
 .........

}
catch (SocketException e )
{
 System.err.println ("Socket error : " + e);
}
catch (UnknownHostException e )
{
 System.err.println ("Invalid host!");
}
catch (IOException e )
{
 System.err.println ("I/O error : " + e);
}
After connecting to port 79 of the finger server, we now have to obtain input and output streams for the socket. We can treat these streams then just as we would file or text input and output. For ease of use we'll covert the input stream into a DataInputStream, and the output stream into a PrintStream. This will allow us to use the readLine and println methods with our socket streams.
// Create input and output streams to socket
PrintStream out = new PrintStream( s.getOutputStream()) ;
DataInputStream in = new DataInputStream(s.getInputStream());
Using our PrintStream out, we write the name of the user we wish to find out information about to the finger server. The server will process the query, and output a result, which we will print to the user's screen.
// Write username to socket output
out.println( username );

// Read response from socket
System.out.println ( in.readLine() );

// Read remaining finger response
String line = in.readLine();

while (line != null)
{
 System.out.println ( line );

 // Read next line
 line = in.readLine();
}
The first line is read from the input stream, and then printed to screen. We then enter a loop, checking to see if there are any more lines to display. The while loop terminates when there are no more bytes available.
Finally, we must close the connection to our finger server. With this last statement, our finger client is complete!
// Terminate connection
s.close();
While the example program functions as a finger client, it can easily be used as a TCP client skeleton, substituting the connection to port 79 with another port matching the application protocol you are trying to use, and modifying the read/write routines to send different data.

Read more...

Sunday, March 28, 2010

UTILITY CLASSES IN JAVA


In this chapter you learn about the java.util package. This package provides some of the most useful Java classes that you will come to rely on. It introduces the following nonabstract classes:
BitSet
Date
Hashtable
Properties
Observable
Random
Vector
Stack
StringTokenizer
The BitSet class is useful for storing and manipulating arbitrarily long sets of bits. The Date class can be used to represent dates and times, and provides methods for converting dates to and from strings. The Hashtable class can be used for creating an array of keys and values and allowing elements to be looked up by either key or value. The Properties class extends Hashtable by allowing elements to be streamed into or out of the class. The Observable class can be extended, and enables you to create new classes that will notify other classes when they change. It works in conjunction with the Observer interface, which is also part of the java.util package.
The Random class is a pseudo-random number generator that can return integer, floating-point, or Gaussian-distributed values. The Stack class is an extension of Vectorand supplies a last-in-first-out (LIFO) data structure. The Vector class can be used for storing any objects and can store objects of more than one type in the same vector. The StringTokenizer class provides a flexible mechanism for parsing strings.

The BitSet Class

The BitSet class represents a dynamically sized set of bits. Two constructors are provided, one that creates an empty set of unspecified size and one that creates a set of a specified size. The set method can be used to set an individual bit or clear can be used to clear an individual bit. The first bit in a BitSet is the zero bit so thatmyBitset.set(0) is a valid statement.
The logical functions andor, and xor are all supported and will combine the BitSet with another set. BitSets can be compared for equality using equals and can be converted to strings using toString. For the purpose of converting a BitSet to a string, a set bit is represented by the value 1 and a clear bit is represented by 0.
The available constructor and nonprivate methods for BitSet are shown in Table 11.1.

Table 11.1. Public constructors and methods of BitSet.



MemberPurpose
BitSet()Constructs an empty bit set.
BitSet(int)Constructs an empty bit set with the specified number of bits.
And(BitSet)Logically ANDs two bit sets.
Clear(int)Clears the specified bit.
Clone()Creates a duplicate copy of the BitSet.
Equals(Object)Returns true if two BitSets are equal.
Get(int)Returns the value of the specified bit.
HashCode()Returns a hash code for the bit set.
Or(BitSet)Logically ORs two bit sets.
Set(int)Sets the specified bit.
Size()Returns the size of the bit set in bits.
ToString()Formats the BitSet as a string.
Xor(BitSet)Logically XORs two bit sets.

An Example

As an example of how to use BitSet consider class EX11A, shown in Listing 11.1. In this example, the method BitSetTest is invoked as the result of the user pressing a button. As various operations on BitSets are performed, a TextArea is updated to display the results of the operations.

import java.applet.*;
import java.awt.*;
import java.util.*;

public class EX11A extends Applet
{
    TextArea results = new TextArea(10, 20);

    public void init()
    {

        add(new Button("Start"));
        add(results);

        resize(320, 240);
    }

    public boolean action(Event evt, Object obj)
    {
        boolean result=false;

        if("Start".equals(obj)) {
            BitSetTest();
            result = true;
        }
        return result;
    }

    void BitSetTest()
    {
        // create a BitSet and set items 1 and 4
        BitSet bits1 = new BitSet(10);
        bits1.set(1);
        bits1.set(4);

        // create a BitSet and set items 4 and 5
        BitSet bits2 = new BitSet(10);
        bits2.set(4);
        bits2.set(5);

        // display the contents of these two BitSets
        results.appendText("Bits 1=" + bits1.toString() + "\r\n");
        results.appendText("Bits 2=" + bits2.toString() + "\r\n");

        // test for equality of the two BitSets
        if(bits1.equals(bits2))
            results.appendText("bits1 == bits2\r\n");
        else
            results.appendText("bits1 != bits2\r\n");

        // create a clone and then test for equality
        BitSet clonedBits = (BitSet)bits1.clone();
        if(bits1.equals(clonedBits))
            results.appendText("bits1 == clonedBits\r\n");
        else
            results.appendText("bits1 != clonedBits\r\n");

        // logically AND the first two BitSets 
        bits1.and(bits2);
        results.appendText("ANDing bits1 and bits2\r\n");
        // and display the resulting BitSet
        results.appendText("bits1=" + bits1.toString() + "\r\n");
    }
}

In the BitSetTest method, two BitSets are constructed. The first, bits1, has bits 1 and 4 set. The second, bits2, has bits 4 and 5 set. The toString method is used to display the contents of the BitSets. The BitSets are then compared using equals. Next, a clone of bits1 is created. To show that the clone method was successful, theBitSets are compared and a message is displayed. Finally, the and method is used to logically AND two BitSets and the result is displayed using toString


NOTE
Because the BitSet class is contained in the package java.util, be sure to import java.util, as done on the third line of Listing 11.1

The Date Class

The Date class stores a representation of a date and time, and provides methods for manipulating the date and time components. As summarized in Table 11.2, constructors are provided that will create a new Date instance based on the current date and time, the number of milliseconds since midnight on January 1, 1970, a string, or from integers representing the year, month, day, hours, minutes, and seconds.

Table 11.2. Constructors for the Date class.



ConstructorPurpose
Date()Creates a Date using today's date.
Date(long)Creates a Date using the specified number of milliseconds since January 1, 1970.
Date(int,int,int)Creates a Date using the specified year, month, and day.
Date(int,int,int,int,
int)
Creates a Date using the specified year, month, day, hours, and minutes.
Date(int,int,int,int,
int,int)
Creates a Date using the specified year, month, day, hours, minutes, and seconds.
Date(String)Creates a Date using the specified string.

As examples of how these constructors can be used to create new Date objects, consider the following:
Date date1 = new Date(); 
Date date2 = new Date(95, 10, 14);
Date date3 = new Date(95, 10, 14, 13, 16, 45);
Date date4 = new Date("14 November 1995 13:16:45");
In this case, date1 will be set to the current date and time. The date2 variable will be set to November 14, 1995. Months are zero-based in the Date class, so 10 is passed as a parameter to indicate the eleventh month. The third example adds more exactness to the second example. While date2 will represent the stroke of midnight on November 14, 1995, date3 is explicitly set to 13:16: 45 (45 seconds after 1:16 p.m.). Finally, date4 creates the same time as date3, but does so from a string.
Many methods are also provided for manipulating Date instances. For example, dates can be compared with the beforeafter, and equals methods. Methods are also provided for converting a Date into various formatted strings. The nonprivate instance methods of the Date class are shown in Table 11.3.

Table 11.3. Nonprivate instance methods of the Date class.



MethodPurpose
after(Date)Returns true if the Date occurs after the specified Date.
Before(Date)Returns true if the Date occurs before the specified Date.
Equals(Object)Returns true if two Dates are equal.
GetDate()Returns the day (1-31) portion of the Date.
GetDay()Returns the day of the week (Sunday is 0)indicated by the Date.
GetHours()Returns the hours (0-23) portion of the Date.
GetMinutes()Returns the minutes (0-59) portion of the Date.
GetMonth()Returns the month (0-11) portion of the Date.
GetSeconds()Returns the seconds (0-59) portion of the Date.
GetTime()Returns the number of milliseconds since midnight on January 1, 1970.
GetTimezoneOffset()Returns the offset in minutes for the current timezone from UTC (Coordinated Universal Time, similar to GMT, Greenwich Mean Time).
GetYear()Returns the year after 1900.
HashCode()Returns a hash code for the Date.
SetDate(int)Sets the day of the month.
SetHours(int)Sets the hours.
SetMinutes(int)Sets the minutes.
SetMonth(int)Sets the month.
SetSeconds(int)Sets the seconds.
SetTime(long)Sets the time to the specified number of milliseconds since midnight January 1, 1970.
SetYear(int)Sets the year after 1900.
ToGMTString()Returns a formatted string of the Date in the GMT time zone.
ToLocaleString()Returns a formatted string of the Date in the current time zone.
ToString()Returns a formatted string of the Date including the day of the week.

In addition to the instance methods shown in Table 11.3, the Date class includes two static methods. Static methods can be invoked without an instance of the class. The static methods of the Date class can be used to determine the number of seconds since midnight on January 1, 1970 based on a string or integer values representing the date and time. These are summarized in Table 11.4; examples of their use are as follows:
long temp = Date.parse("14 November 1996");
Date date1 = new Date(temp);
temp = Date.UTC(96, 10, 14, 13, 16, 45);
Date date2 = new Date(temp);


Table 11.4. Static methods of the Date class.



MethodPurpose
UTC(int,int,int,int,
int,int)
Returns the milliseconds since midnight January 1, 1970 based on the year, month, day, hours, minutes, and seconds parameters.
Parse(String)Returns the milliseconds since midnight January 1, 1970 based on parsing the supplied string.

As an example of the use of the Date class, consider example EX11B, as shown in Listing 11.2. The init method of this class displays a TextArea and then creates a Date object based on the current date. The toString, toLocaleString, and toGMTString methods are used to display the current date in various formats. An instance named BastilleDay is then constructed using the date of that holiday. Next, the year value for BastilleDay is set to the current year using the setYear and getYear methods. Finally, after is used to determine whether Bastille Day has already occurred this year, or if there is still time to shop for presents for your French friends. The results of executing this class are shown in Figure 11.1.
import java.applet.*;
import java.awt.*;
import java.util.*;

public class EX11B extends Applet
{
    TextArea results = new TextArea(10, 50);

    public void init()
    {
        add(results);

        Date today = new Date(); // today

        // display the current date in a couple of different formats
        results.appendText("Today is:" + today.toString() + "\r\n");
        results.appendText("Locale Time:" + today.toLocaleString() + "\r\n");
        results.appendText("GMT:" + today.toGMTString() + "\r\n");

        // store Bastille Day (July 14th) in an instance
        Date BastilleDay = new Date(96, 6, 14);    // 7-14-96

        // set Bastille Day to be in the current year
        BastilleDay.setYear(today.getYear());

        // see if we've already missed Bastille Day
        if (today.after(BastilleDay))
            results.appendText("You missed Bastille Day!\r\n");
        else
            results.appendText("Bastille Day is coming!\r\n");

        resize(320, 240);
    }
}

The Hashtable Class

The Hashtable class extends the abstract Dictionary class that is also defined in the java.util package. A Hashtable is used for mapping keys to values. For example, it could be used to map names to ages, programmers to projects, job titles to salaries, and so on.
Hashtable will expand in size as elements are added to it. When creating a new Hashtable you can specify an initial capacity and a load factor. The Hashtable will increase in size whenever adding a new element would move the Hashtable past its threshold. A Hashtable's threshold is its capacity multiplied by its load factor. For example, a Hashtable with a capacity of 100 and a load factor of 0.75 would have a threshold of 75 items. The constructors for Hashtable are shown in Table 11.5.

Table 11.5. Constructors for the Hashtable class.



ConstructorPurpose
Hashtable(int)Constructs a new Hashtable with the specified initial capacity.
Hashtable(int, float)Constructs a new Hashtable with the specified initial capacity and load factor.
Hashtable()Constructs a new Hashtable using default values for initial capacity and load factor.

As an example of how to construct a new Hashtable, consider the following:
Hashtable hash1 = new Hashtable(500, .80);
In this case a Hashtable is constructed that will hold 500 elements. When the Hashtable becomes 80 percent full (a load factor of .80), its maximum size will be increased.
Each element in a Hashtable consists of a key and value. Elements are added to a Hashtable using the put method and are retrieved using get. Elements may be deleted from a Hashtable with remove. The contains and containsKey methods can be used to look up a value or key in the Hashtable. These and other Hashtablemethods are summarized in Table 11.6.

Table 11.6. Nonprivate methods of the Hashtable class.



MethodPurpose
clear()Removes all elements from the Hashtable.
clone()Creates a clone of the Hashtable.
contains(Object)Returns true if the Hashtable contains the specified object.
containsKey(Object)Returns true if the Hashtable contains the specified key.
elements()Returns an enumeration of the elements in the Hashtable.
get(Object)Retrieves the object associated with the specified key.
isEmpty()Returns true if the Hashtable is empty.
keys()Returns an enumeration of the keys in the Hashtable.
put(Object, Object)Adds a new element to the Hashtable using the specified key and value.
rehash()Rehashes the Hashtable into a larger Hashtable.
remove(Object)Removes the object given by the specified key.
size()Returns the number of elements in the Hashtable.
toString()Returns a formatted string representing the Hashtable.

Class EX11C, shown in Listing 11.3, demonstrates how to use the Hashtable class. In this example a new Hashtableht, is constructed and is used to store the five best albums released by Pink Floyd. The Hashtable is displayed to a TextArea using toString.

import java.applet.*;
import java.awt.*;
import java.util.*;

public class EX11C extends Applet
{
    TextArea results = new TextArea(10, 75);

    public void init()
    {
        add(results);

        // create a new Hashtable
        Hashtable ht = new Hashtable();

        // add Pink Floyd's best albums
        ht.put("Pulse", new Integer(1995));
        ht.put("Dark Side of the Moon", new Integer(1973));
        ht.put("Wish You Were Here", new Integer(1975));
        ht.put("Animals", new Integer(1977));
        ht.put("Ummagumma", new Integer(1969));

        // display the Hashtable
        results.appendText("Initailly: "+ht.toString() + "\r\n");

        // test for any album from 1969
        if (ht.contains(new Integer(1969)))
            results.appendText("An album from 1969 exists\r\n");

        // test for the Animals album
        if (ht.containsKey("Animals"))
            results.appendText("Animals was found\r\n");

        // find out what year Wish You Were Here was released
        Integer year = (Integer)ht.get("Wish You Were Here");
        results.appendText("Wish You Were Here was released in " +
                year.toString() + "\r\n");

        // remove an album
        results.appendText("Removing Ummagumma\r\n");
        ht.remove("Ummagumma");

        // move through an enumeration of all keys in the table
        results.appendText("Remaining:\r\n");
        for (Enumeration enum=ht.keys(); enum.hasMoreElements() ;)
            results.appendText((String)enum.nextElement()+"\r\n");

        // and resize the applet window
        resize(500, 240);
    }
}

In this Hashtable, the names of the albums are the keys and the years are the elements. Therefore, contains is used to look up the integer element 1969 to see whether the list contains any albums from 1969. Similarly, containsKey is used to search for the key "Animals" to see if that album made the list. Next, the get method is used to see whether "Wish You Were Here" is in the Hashtable. Because get returns the element associated with the key, both the name and year are displayed at this point. This can be seen in Figure 11.2.
Next, I had some second thoughts about including "Ummagumma" in a list of best albums and used remove to delete it from the Hashtable. Finally, the keys method is used to create an Enumeration of the keys stored in the Hashtable. The applet then iterates through the enumeration using enum.hasMoreElements andenum.nextElement.

The Properties Class

The Properties class extends Hashtable and adds the capability to read and write a Hashtable to a stream. Because an applet cannot access files, the Propertiesclass is useful only for applications.
Like Hashtable, this class can be used to store keys and associated values. Through its save and load methods, Properties can be written to disk. This makes this class an excellent mechanism for storing configuration information between runs of a program. An example of a Properties file written by the save method is as follows:
#This is a header comment
#Sat Sep 14 15:55:15 Pacific Daylight Time 1996
prop3=put three
prop2=put two
prop1=put one
Because Properties is a subclass of Hashtable, new key/value pairs are added using the put method of Hashtable. The constructors and nonprivate methods ofProperties are shown in Table 11.7.

Table 11.7. Nonprivate constructors and methods of Properties class.



MemberPurpose
Properties()Creates a new Properties object.
Properties(Properties)Creates a new Properties object based on the specified default values.
GetProperty(String)Returns the property value associated with the specified key.
GetProperty(String,String)Returns the property value associated with the specified key. If the key is not found, the specified default is returned.
List(PrintStream)Lists all of the properties to the specified PrintStream.
Load(InputStream)Reads a set of properties from the specified InputStream.
PropertyNames()Returns an Enumeration of all property names.
Save(OutputStream, String)Saves the Properties and a header string to the specified OutputStream.

Example EX11D, shown in Listing 11.4, shows how to create an instance, put three properties into it, save it, and then reload the keys and values into a different instance.

import java.awt.*;
import java.io.*;
import java.util.*;

public class EX11D
{

    public static void main(String[] args)
    {
        // create a new instance
        Properties props1 = new Properties();

        // put three properties into it
        props1.put("prop1", "put one");
        props1.put("prop2", "put two");
        props1.put("prop3", "put three");

        // retrieve each of the three properties
        String prop1 = props1.getProperty("prop1", "one");
        String prop2 = props1.getProperty("prop2", "two");
        String prop3 = props1.getProperty("prop3");

        // save the properties to a file
        try 
        {
            props1.save(new FileOutputStream("test.ini"), "My header");
        }
        catch (IOException e) {
            return;
        }

        // create a new instance and read the file in from the file
        Properties props2 = new Properties();
        try
        {
            FileInputStream inStr = new FileInputStream("test.ini");
            props2.load(inStr);
        }
        catch (IOException e) {
            return;
        }

        // retrieve a property from the second instance
        String prop = props2.getProperty("prop2", "two");
    }
}

The Random Class

This class represents a pseudo-random number generator. Two constructors are provided, one taking a seed value as a parameter and the other taking no parameters and using the current time as a seed. Constructing a random number generator with a seed value is a good idea unless you want the random number generator to always generate the same set of values. On the other hand, sometimes it is useful to generate the same sequence of "random" numbers. This is frequently useful while debugging a program.
Once the random number generator is created, a value can be retrieved from it using any of the following methods:
nextDouble
nextFloat
nextGaussian
nextInt
nextLong
The constructors and methods of Random are summarized in Table 11.8. Use of the Random class is very simple, so no example is presented here. However, exampleEX11E, shown in the following discussion of the Observer class, also uses Random.

Table 11.8. Nonprivate constructors and methods of Random class.



MethodPurpose
Random()Creates a new random number generator.
Random(long)Creates a new random number generator based on the specified seed value.
nextDouble()Returns the next double value between 0.0D and 1.0D from the random number generator.
nextFloat()Returns the next float value between 0.0F and 1.0F from the random number generator.
NextGaussian()Returns the next Gaussian-distributed double from the random number generator. Generated Gaussian values will have a mean of 0 and a standard deviation of 1.0.
nextInt()Returns the next integer value from the random number generator.
nextLong()Returns the next long value from the random number generator.
setSeed(long)Sets the seed value for the random number generator.

Observers and Observables

An Observable class is a class that may be watched or monitored by another class that implements the Observer interface. Associated with each Observable instance is a list of Observers. Whenever the Observable instance changes it can notify each of its Observers. By using Observable and Observer classes you can achieve a better partitioning of your code by decreasing the reliance of one class or another.

The Observable Class

The Observable class includes a single constructor that takes no parameters. It also includes a number of methods for managing its list of Observers. These are summarized in Table 11.9.

Table 11.9. Nonprivate constructors and methods of Observer class.



MemberPurpose
Observable()Creates a new Observable instance.
addObserver(Observer)Adds an Observer to the list of objects observing this instance.
clearChanged()Clears the internal flag used to indicate that an Observable object has changed.
countObservers()Returns the number of Observers of this object.
deleteObserver(Observer)Deletes an Observer from the list of Observers of this object.
deleteObservers()Deletes all Observers of this object.
hasChanged()Returns true if the object has changed.
notifyObservers()Notifies all Observers that a change has occurred in the Observable object.
notifyObservers(Object)Notifies all Observers that a change has occurred and passes them the specified argument.
setChanged()Sets the internal flag to indicate that a change has occurred.

The Observer Interface

The Observer interface defines an update method that is invoked by an Observable object whenever the Observable object has changed and wants to notify itsObservers. The signature of update is as follows:
public abstract void update(Observable o, Object arg);
This method is called whenever an Observable instance that is being observed invokes either of its notifyObservers methods. It is passed the Observable object that changed and an optional additional parameter.

An Example

As an example of how Observable can be used, consider the declaration of the Obsable class in Listing 11.5. The Obsable class stores a secret number that is generated by a random number generator. Whenever the secret number changes, all Observers are notified.

import java.applet.*;
import java.awt.*;
import java.util.*;

// define a class that stores a secret number
class Obsable extends Observable {
    private int secretNumber;
    private Random generator;

    public Obsable(int seed) {
        // create a random number generator that will
        // make the "secret numbers"
        generator = new Random(seed);
    }

    public void GenerateNumber() {
        // generate a new secret number
        secretNumber = generator.nextInt();
        // indicate to Observable that the instance has changed
        setChanged();
        // notify all of the observers and pass the new number
        notifyObservers(new Integer(secretNumber));
    }
}


public class EX11E extends Applet implements Observer
{
    Integer secretNumber;
    Obsable obs;
    TextField tf;

    public EX11E() 
    {
        // create a new instance of the observable class
        obs = new Obsable(12);
        // indicate that "this" is an observer
        obs.addObserver(this);

        // create a read-only TextField
        tf = new TextField(10);
        tf.setEditable(false);
    }

    public void init() {
        add(new Label("Secret Number:"));
        add(tf);
        add(new Button("Change"));
        resize(320, 240);
    }

    // this method is invoked when the observable object
    // notifies its observers
    public void update(Observable o, Object arg) {
        // store the secret number and display it
        secretNumber = (Integer)arg;
        tf.setText(String.valueOf(secretNumber));
    }

    public boolean action(Event evt, Object obj)
    {
        boolean result=false;

        if("Change".equals(obj)) {
            obs.GenerateNumber();
            result = true;
        }
        return result;
    }
}

No Observable class is complete without an Observer, so Listing 11.5 also includes class EX11E, which extends Applet and implements the Observer interface. The results of executing this applet are shown in Figure 11.3. The constructor for EX11E creates an instance of Obsable, the Observable class defined previously and adds the applet as an observer of the Observable object. The init method places a label, TextField, and button on the screen. The TextField will be used to display the current value of the secret number.
The EX11E class contains an update method that is part of the Observer interface. This method is passed an Observable item and an Object as parameters. As shown in the declaration of Obsable, the Object is an integer. Therefore, update casts the Object into an Integer, stores it in the instance variable secretNumber, and then updates the TextField.
The action method traps pushes of the Change button and then calls obs.GenerateNumber to generate a new random number. Each time GenerateNumber changes the secret number, the update method in the observer will be called. In this case, this will update the display with each newly generated random number.

The Vector Class

One of the problems with an array is that you must know how big it must be when you create it. This is not always practical and is rarely easy. Imagine what would have happened if the founding fathers of the United States were programmers and they used a 13-element array to hold the names of all the states. Even if the lead programmer on this project (Thomas Jefferson, if I remember my eighth grade history correctly) had the foresight to double or triple the array size to allow for future growth, it still would not have been enough.
The Java Vector class solves this problem by providing a form of resizable array that can grow as more elements are added to it. A Vector stores items of type Object so that it can be used to store instances of any Java class. A single Vector may store different elements that are instances of different classes.
At any point in time a Vector has the capacity to hold a certain number of elements. When a Vector reaches its capacity, its capacity is incremented by an amount specific to that Vector. The Vector class provides three different constructors that enable you to specify the initial capacity and increment quantity of a Vector when it is created. These constructors are summarized in Table 11.10.

Table 11.10. Constructors for the Vector class.



ConstructorPurpose
Vector(int)Creates a new Vector with the specified initial capacity.
Vector(int, int)Creates a new Vector with the specified initial capacity and increment quantity.
Vector()Creates a new Vector with defaults for the initial capacity and increment quantity.

An item is added to a Vector using addElement. Similarly, an element can be replaced using setElementAt. A Vector can be searched using the contains method, which simply looks for an occurrence of an Object. The elements method is useful because it returns an enumeration of the Objects stored in the Vector. These and other member methods of Vector are summarized in Table 11.11.

Table 11.11. Nonprivate methods of the Vector class.



MethodPurpose
addElement(Object)Inserts the specified element into the Vector.
capacity()Returns the number of elements that will fit into the currently allocated portion of the Vector.
clone()Clones the vector, but not its elements.
contains(Object)Returns true if the Vector contains the specified Object.
copyInto(Object [])Copies the elements of the Vector into the specified array.
elementAt(int)Retrieves the element located at the specified index.
elements()Returns an enumeration of the elements in the Vector.
ensureCapacity(int)Ensures that the Vector can hold at least the specified minimum capacity.
firstElement()Returns the first element in the Vector.
indexOf(Object)Searches the Vector and returns the zero-based index of the first matchingObject.
indexOf(Object, int)Searches the Vector beginning at the specified index number and returns the zero-based index of the next matching Object.
insertElementAt(Object, int)Adds the specified Object at the specified index.
isEmpty()Returns true if the Vector has no elements.
LastElement()Returns the last element in the Vector.
lastIndexOf(Object)Searches the Vector and returns the zero-based index of the last matchingObject.
lastIndexOf(Object, int)Searches the Vector beginning at the specified index number and returns the zero-based index of the prior matching Object.
removeAllElements()Removes all elements from the Vector.
removeElement(Object)Removes the specified Object from the Vector.
removeElementAt(int)Removes the Object at the specified index.
setElementAt(Object, int)Replaces the Object at the specified index with the specified Object.
setSize(int)Sets the size of the Vector to the specified new size.
size()Returns the number of elements currently in the Vector.
toString()Returns a formatted string representing the contents of the Vector.
trimToSize()Removes any excess capacity in the Vector by resizing it.

As an example of using Vector consider example class EX11F, shown in Listing 11.6. This example again uses a TextArea to display the results of various operations. In this example, a Vectorv1, is constructed. Initially, enough space is reserved for 10 elements and the Vector will increase its capacity by four whenever there is no room for a new element.

import java.applet.*;
import java.awt.*;
import java.util.*;

public class EX11F extends Applet
{
    TextArea results = new TextArea(10, 75);

    public void init()
    {
        resize(500, 240);
        add(results);

        // create a new Vector to hold 10 elements
        // and to increase by 4 each time it's necessary
        Vector v1 = new Vector(10, 4);

        // add elements, both Integer and String, to the Vector
        v1.addElement(new Integer(1));
        v1.addElement(new Integer(2));
        v1.addElement(new Integer(3));
        v1.addElement("Four");
        v1.addElement(new Integer(5));

        // display the entire Vector
        results.appendText(v1.toString() + "\r\n");

        // see if the Vector contains this Integer
        if (v1.contains(new Integer(2)))
            results.appendText("It contains 2\r\n");

        // see if the Vector contains this String
        if (v1.contains("Four"))
            results.appendText("It contains Four\r\n");

        // Display the capacity of the Vector
        int capacity = v1.capacity();
        results.appendText("Can hold " +
                String.valueOf(capacity) + "\r\n");

        // Display the element at index number 3
        results.appendText("ElementAt 3 = " +
                (String)v1.elementAt(3) + "\r\n");

        // clear out the Vector
        v1.removeAllElements();

        // add the names of Pink Floyd's first five albums
        v1.addElement("Piper At The Gates of Dawn");
        v1.addElement("Saucerful of Secrets");
        v1.addElement("Ummagumma");
        v1.addElement("Meddle");
        v1.addElement("The Dark Side of the Moon");

        // use an enumeration to display each of the album titles
        for (Enumeration enum = v1.elements();
                enum.hasMoreElements(); )
            results.appendText((String)enum.nextElement()+"\r\n");
    }

}

Initially, five elements-four integers and one string-are added to the Vector. This illustrates the capability to store objects of different types in the same Vector. Next, thetoString method is used to display the entire Vector. This, along with the other messages displayed by EX11F, can be seen in Figure 11.4.
Next, the contains method is used to determine if the Vector contains the Integer 2 and the string "Four". The capacity of the Vector is then displayed and the item at index number three is retrieved and displayed. Because Vector is zero-based this will retrieve the fourth element in the Vector.
The method removeAllElements is then used to remove the current elements. The names of Pink Floyd's first five albums are then added to the Vector. Finally, theelements method is used to return an Enumeration of the elements. The hasMoreElements and nextElement method are used in a loop that displays each of the album titles.

The Stack Class

The Stack class extends Vector and implements a simple last-in-first-out stack. An item is stored on a stack by "pushing" it onto the stack. An item may subsequently be "popped" off the stack and used. The item popped off a stack will always be the most recently pushed item.
Because Stack extends the Vector class, no size is associated with a Stack instance. The Stack will continue to grow in size as new items are pushed onto it. In addition to methods to push and pop items, a peek method is provided for looking at the next item, a search method is provided for scanning the Stack for a specific item, and anempty method is provided determining whether more items are stored in the Stack.
The single constructor as well as the nonprivate methods of Stack are summarized in Table 11.12.

Table 11.12. Constructors and nonprivate methods of the Stack class.



MemberPurpose
Stack()Creates a new Stack.
empty()Returns true if the Stack is empty.
peek()Returns the last Object added to the Stack but does not remove it from the Stack.
pop()Returns the last Object added to the Stack and removes it.
push(Object)Adds the specified Object to the Stack.
search(Object)Searches for the specified Object in the Stack.

An example of using Stack is provided in example EX11G, shown in Listing 11.7. In this example a Stack is created and three items are added to it. The toString method is used to display the Stack's initial contents. The peek method is used to look at the last object added to the Stack but does not actually remove the object. Next, searchis used to look for an occurrence of "2" within the Stack. Finally, pop is used to remove an object from the Stack and toString is used to redisplay its contents. The results of executing this applet are shown in Figure 11.5.
import java.applet.*;
import java.awt.*;
import java.util.*;

public class EX11G extends Applet
{
    TextArea results = new TextArea(10, 30);

    public void init()
    {
        resize(320, 240);
        add(results);

        // create a new Stack
        Stack stk = new Stack();

        // add three items to the Stack
        stk.push("1");
        stk.push("2");
        stk.push("3");

        // display the entire Stack
        results.appendText("Stack=" + stk.toString() + "\r\n");

        // peek at what's next off the stack
        String str = (String)stk.peek();
        results.appendText("Peeked at: " + str + "\r\n");

        // see if there's a "2" anywhere in the Stack
        if (stk.search("2") != -1)
            results.appendText("Found 2\r\n");

        // pop an item off the Stack
        str = (String)stk.pop();
        results.appendText("Popped: " + str + "\r\n");

        // display the entire Stack
        results.appendText("Stack=" + stk.toString() + "\r\n");
    }
}

The StringTokenizer Class

StringTokenizer can be used to parse a string into its constituent tokens. For example, each word in a sentence could be considered a token. However, theStringTokenizer class goes beyond the parsing of sentences. You can create a fully customized tokenizer by specifying the set of token delimiters when theStringTokenizer is created. For parsing text, the default whitespace delimiters are usually sufficient. However, you could use the set of arithmetic operators (+*/, and-) if parsing an expression.
The delimiter characters can be specified when a new StringTokenizer object is constructed. Table 11.13 summarizes the three available constructors. Examples of using these constructors are the following:
StringTokenizer st1 = new StringTokenizer("A stream of words");
StringTokenizer st2 = new StringTokenizer("4*3/2-1+4", "*+/-", true);
StringTokenizer st3 = new StringTokenizer("aaa,bbbb,ccc", ",");
In the first example, the st1 StringTokenizer will be constructed using the supplied string and the default delimiters. The default delimiters are the space, tab, newline, and carriage return characters. These delimiters are useful when parsing text, as with st1. The second example constructs a StringTokenizer for tokenizing arithmetic expressions using the *+/, and - symbols as supplied. Finally, the third StringTokenizerst3, will tokenize the supplied string using only the comma character as a delimiter.

Table 11.13. Constructors for the StringTokenizer class.



ConstructorPurpose
StringTokenizer(String)Creates a new StringTokenizer based on the specified string to be tokenized.
StringTokenizer(String, String)Creates a new StringTokenizer based on the specified string to be tokenized and set of delimiters.
StringTokenizer(String,String,
boolean)
Creates a new StringTokenizer based on the specified string to be tokenized, set of delimiters, and a flag that indicates if delimiters should be returned as tokens.

Because StringTokenizer implements the enumeration interface it includes the hasMoreElements and nextElement methods. Additionally, the methodshasMoreTokens and nextToken are provided and perform the same operations. The nonprivate methods of StringTokenizer are summarized in Table 11.14.

Table 11.14. Nonprivate methods of the StringTokenizer class.



MethodPurpose
countTokens()Returns the number of remaining tokens.
hasMoreElements()Returns true if there are more elements in the string being tokenized. It is identical tohasMoreTokens.
hasMoreTokens()Returns true if there are more tokens in the string being tokenized. It is identical tohasMoreElements.
nextElement()Returns the next element in the string. It is identical to nextToken.
nextToken()Returns the next token in the string. It is identical to nextElement.
nextToken(String)Changes the set of delimiters to the specified string and then returns the next token in the string.

Class EX11H is an example of how to use the StringTokenizer class and is shown in Listing 11.8. In this example two StringTokenizer objects are created. The first,st1, is used to parse an arithmetic expression. The second, st2, parses a line of comma-delimited fields. For both tokenizers, hasMoreTokens and nextToken are used to iterate through the set of tokens, displaying each token in a TextArea. The results of executing EX11H are shown in Figure 11.6.
import java.applet.*;
import java.awt.*;
import java.util.*;

public class EX11H extends Applet
{
    TextArea results = new TextArea(14, 25);

    public void init()
    {
        resize(320, 240);
        add(results);

        // put an arithmetic expression in a string
        // and create a tokenizer for the string
        String mathExpr = "4*3+2/4";
        StringTokenizer st1 = new StringTokenizer(mathExpr, 
                "*+/-", true);

        // while there are tokens left, display each token
        results.appendText("Tokens of mathExpr:\r\n");
        while (st1.hasMoreTokens()) 
            results.appendText(st1.nextToken() + "\r\n");

        // create a String of comma-delimited fields
        // and create a tokenizer for the string
        String commas = "field1,field2,field3,and field 4";
        StringTokenizer st2=new StringTokenizer(commas,",",false);

        // while there are tokens left, display each token
        results.appendText("Comma-delimited tokens:\r\n");
        while (st2.hasMoreTokens()) 
            results.appendText(st2.nextToken() + "\r\n");
    }
}

Read more...

Saturday, March 27, 2010

MULTI THREADING

What Is a Thread?

A thread executes a series of instructions. Every line of code that is executed is done so by a thread. Some threads can run for the entire life of the applet, while others are alive for only a few milliseconds. A thread also creates a new thread or kills an existing one. Threads run in methods or constructors. The methods and constructors themselves are lifeless. The threads go into the methods and follow their instructions. Methods and constructors reside in the computer's memory. Figure 16.1 shows threads, constructors, and methods in a typical applet.
The applet methods start()paint(), and so on are all called by underlying threads. These threads are created by the Web browser. When there is a mouse click or arepaint() has been called, the underlying thread calls the appropriate thread in the applet. While the threads are running in the applet methods, they cannot do anything else. If the applet holds these threads too long, it locks up the browser. If an applet needs to do something that will take some time in one of its methods, it should start a new thread. Some specific uses for threads are listed below:
  • Long initiations.  Threads are used in applets that may take a while to initialize. The applet may need to do something like wait for an image to be loaded. A thread is used so that the system thread can handle other events.
  • Repetitive or timed tasks.  Threads are used to do tasks that happen repetitively. A common example of this is found in animations. Every few milliseconds a new frame is shown. A thread displays a frame, sleeps for a while, and then repeats the process.
  • Asynchronous events.  Threads are used to handle events. An example of this is a mouse click. If the user clicks the mouse, a new thread is created to render a new frame in an image.
  • Multiple tasks.  Threads are used to do more than one thing at once. One thread controls an animation, while another does a computation.

The Thread Class

The class java.lang.Thread is used to create and control threads. To create a thread, a new instance of this class must be created. However, the thread does not start running right away. Thread.start() must be called to actually make the thread run. When Thread.start() is called, the thread begins executing in the run() method of the target class. A new Thread class always starts running the public void run() method of a class. There are two ways to create a thread:
  • Extend the Thread class.  With this technique the new class inherits from the class Thread. The thread can start running in the class's run method.
  • Implement the Runnable interface.  This technique is probably more common than extending the Thread class. It is not necessary to define a new class to run the thread. If a thread is to start running in the applet, it must use the Runnable interface. The applet cannot inherit from both the Thread and Applet classes. An applet with the Runnable interface must have a run() method for the thread to start.
There isn't much difference between the two approaches. Both extending the Thread class and implementing the Runnable interface have the same functionality. The interface approach must be used if the thread is to actually start in the applet class. But if the thread is going to be running in its own class, it may be more convenient to extend the Thread class. Examples of both approaches are in this chapter.
The Thread class has seven constructors. All of them create a new thread. The thread does not start running until Thread.start() is called. When Thread.start() is called, the new thread starts running in the run() method of an object. The constructors are the following:
Thread()
Thread(Runnable)
Thread(ThreadGroup)
Thread(String)
Thread(ThreadGroup,String)
Thread(Runnable,String)
Thread(ThreadGroup,Runnable,String)
The constructors can use three possible parameters:
  • String  The name of the new thread is the parameter String. A thread can get its name by calling Thread.getName().
  • ThreadGroup  The new thread will belong to the group specified by the parameter ThreadGroup. A ThreadGroup can be used to organize a thread.
  • Runnable  The Runnable parameter is an object that has implemented the Runnable interface. The thread will start executing in the run() method of the Runnableparameter when Thread.start() has been called.
There are many methods in the Thread class. Some of them, such as destroy(), don't seem to have been implemented yet, and may never be. Some of the methods that control the thread execution are the following:
  • start()  This method starts the thread. It starts executing in the run() method of its Runnable target that was set when the constructor was called. This method can be called only once.
  • suspend()  This method suspends the execution of the thread. It remains suspended until resume() is called.
  • resume()  This method resumes the execution of a suspended thread. It has no effect on a thread that is not suspended.
  • stop()  This method stops and kills a running thread. Currently, the thread does not stop unless it is running. If it is suspended, it does not die until it starts running again. However, this may be fixed someday.
  • sleep(int m)/sleep(int m,int n)  The thread sleeps for m milliseconds, plus n nanoseconds.

Simple Thread Examples

Listing 16.1 shows how to start, stop, suspend, and resume threads. It uses the Runnable interface. Threads like this are useful for things like controlling animation sequences or repeatedly playing audio samples. This example uses a thread that counts and prints a string every second. The thread starts when the applet is initialized. It continues to run until the user leaves the page. If the user returns to the page (and all is well), the thread continues from where it left off. This allows applets to retain their states while the user is away.

import java.lang.Thread;
import java.applet.Applet;

public class InfiniteThreadExample extends Applet implements Runnable
{
  Thread myThread;

  public void init()
    {
      System.out.println("in init() -- starting thread.");
      myThread= new Thread(this);
      myThread.start();
    }

  public void start()
    {
      System.out.println("in start() -- resuming thread.");
      myThread.resume();
    }

  public void stop()
    {
      System.out.println("in stop() -- suspending thread.");
      myThread.suspend();
    }

  public void destroy()
    {
      System.out.println("in destroy() -- stoping thread.");
      myThread.resume();
      myThread.stop();
    }

  public void run()
    {
      int i=0;
      for(;;)
        {
          i++;
          System.out.println("At " + i + " and counting!");
          try {myThread.sleep(1000);}
          catch (InterruptedException e ) {}
        }
    }
}

SimpleThreadExample Output

The output of InfiniteThreadExample is shown here. The applet ran for nine seconds until it was stopped.
in init() -- starting thread.
At 1 and counting!
in start() -- resuming thread.
At 2 and counting!
At 3 and counting!
At 4 and counting!
At 5 and counting!
At 6 and counting!
At 7 and counting!
At 8 and counting!
At 9 and counting!
in stop() -- suspending thread.
in destroy() -- stoping thread.
The applet has only five methods:
  • public void init()  The thread is initialized and is started in this method. In this example, the constructor Thread takes the argument this. When theThread.start() method is called, the thread looks for a public void run() method in the this object.
  • public void start()  When this method is called by the system, the thread resumes execution. If the thread is already running, this method has no effect.
  • public void stop()  This method suspends the thread.
  • public void destroy()  This method stops the thread. Thread.stop() stops and kills the thread. However, it only kills a thread that is running, soThread.resume() is called beforehand.
  • public void run()  This is where the thread actually starts running. This example has an infinite loop that prints a string and then sleeps for a second. Long running threads should sleep every once in a while to give other threads a chance to run. If not, the system may not even get a chance to paint the applet.

When Are the Methods in InfiniteThreadExample Called?

Unfortunately, its not always possible to know exactly when or if the methods are called. It can vary from browser to browser, or even by how the user has the browser configured. Netscape 3.0 calls init() and then calls start() the first time the applet is loaded. If the user leaves the page with the applet, stop() is called. Returning to the applet calls start(), but it is possible that init() may be the first called. It depends on whether or not the applet is still residing in memory. If it is, then only start() is called; otherwise, both init() and start() are called again.
The method destroy() is called before the applet is removed from memory. All threads should be destroyed at this time so that its resources can be used by other applets. The browsers can only handle so many threads. If the user visits too many applets that don't clean up after themselves, the browser may crash. Generally, threads should be suspended when the user leaves the page and killed when the applet is destroyed. It is possible to leave threads running while the user is visiting other pages, but the user may not appreciate it.
Listing 16.2 shows how to use threads to handle events. When an event that existing threads shouldn't take the time to handle happens in the applet, a new thread is spawned to handle that event. After the event is handled, the new thread quietly dies. Listing 16.2 uses threads to handle mouse clicks. Each thread draws a blue target, as you can see in Figure 16.2. Methods such as mouseDown() or mouseUp() are called by threads external to the applet. While the thread is running in the applet, no other mouse movements are detected. Keeping the external thread may not just affect the applet, but possibly the whole browser. Generally, these methods should be returned as soon as possible. If it is necessary to do a long computation or wait for something else, a new thread should be started. By starting a new thread, the external thread can return almost immediately.

import java.applet.Applet;
import java.awt.*;

public class FiniteThreadExample extends Applet
{
  Image offImage;        /* off screen image      */
  Graphics offGraphics;  /* Graphics for offImage */

  public void init()
    {
      offImage=createImage(400,300);
      offGraphics=offImage.getGraphics();
    }

  public void paint(Graphics g)
    {
      g.drawImage(offImage,0,0,null);
    }

  public void update(Graphics g)
    {
      paint(g);
    }

  public boolean mouseDown(Event e, int x, int y)
    {
      new DrawTarget(this,x,y,offGraphics);
      return true;
    }
}

class DrawTarget extends Thread
{
  int xPos,yPos;        /* position of the target */
  Applet myMaster;      /* who to repaint         */
  Graphics offGraphics; /* Graphics to draw on    */

  public DrawTarget(Applet a, int x, int y, Graphics g)
    {
      xPos=x; yPos=y;
      myMaster=a;
      offGraphics=g;
      start();
    }

  public void run()
    {
      int i;  /* i is direction the circles are moving */
      int r;  /* r is the radius of the current circle */

      offGraphics.setColor(Color.white);
      offGraphics.setXORMode(Color.blue);
      for(r=0,i=10;i>-20;i-=20)      /* i=(10,-10)             */
        for(r+=i;(r<90)&&(r>0);r+=i) /* r=(10,20...80,80,70...10) */
          {
            offGraphics.fillOval(xPos-r,yPos-r,2*r,2*r);
            myMaster.repaint();
            try {sleep(200);}
            catch (InterruptedException e) {}
          }
    }
}

The class FiniteThreadExample is used to paint the applet, to catch the mouse clicks, but not to start the threads. The applet uses a class that extends the Thread class to start new threads. The class FiniteThreadExample has four methods shown below that sets up things, handles the painting, and catches the mouse clicks:
  • public void init()  This method creates an image and gets a Graphics context for that image.
  • public void paint(Graphics)  This method paints the Image offImage on the screen.
  • public void update(Graphics)  This method isn't really necessary. It overrides update(Graphics) in java.awt.Component, and is used to reduce flickering.
  • public boolean mouseDown(Event, int, int)  This method is called when there is a mouse click in the applet. It creates a new instance of the classDrawTargetDrawTarget is defined in the next class.
DrawTarget is where the threads are created to draw the targets. DrawTarget inherits properties from java.lang.Thread and has a single constructor and method, which are listed below:
  • public DrawTarget(Applet a, int x, int y, Graphics g)  The constructor copies the parameters to instance variables and starts the thread. Applet is needed so the run method can tell the applet to repaint. The integers x and y are the coordinates of target. Graphics is the graphics context on which the targets are drawn. The thread is started by simply calling start().
  • public void run()  This method is where the thread starts and draws the targets. It is called sometime after start() is called in the constructor. The method first sets offGraphics to XOR-Mode. In this mode, if something is drawn on something previously drawn, it reverts back to its original color. Next, the thread enters the nested for loops. Each iteration draws a circle, asks the applet to repaint, and sleeps for 200ms. The radius of the circle is varied from 10 to 80, and then from 80 back to 10. The thread dies on its own after it exits the loops, so there is no need to call stop().

Problems with Multithreading

Listing 16.3 shows how data can be corrupted in a multithreaded environment. If more than one thread manipulates shared variables or objects at the same time, corruption may result. Instance variables are shared between threads. If one is modified by a thread, the change affects the other threads. Method variables are unique for each threads. Each Thread has its own copy. Listing 16.3 uses 2 classes, ProblemThreadExample and CorruptedDataExample. The class ProblemThreadExample extends theApplet class. It has two methods, which are listed after Listing 16.3.

import java.lang.*;
import java.applet.Applet;

public class ProblemThreadExample extends Applet
{
  CorruptedDataExample CDE;

  public void start()
    {
      int i;

      CDE=new CorruptedDataExample();
      for(i=0;i<20;i++)  /* start 20 threads */
        new Thread(CDE,new String("booThread"+i)).start();
    }

  public void stop()
    {
      CDE.stopThreads();
    }
}

class CorruptedDataExample extends Object implements Runnable
{
  int num=0;  /* num will be corrupted  */

  public void run()
    {
      int i=0;
      for(;;)
        {
          for(i=0;i<1000;i++)
            {
              num=num+10;
              num=num-10;
            }

          try {Thread.sleep(10000);}
          catch (InterruptedException e ) {}
          System.out.println(Thread.currentThread().getName()+
                     " sees the number: " + num);
        }
    }

  void stopThreads()
    {
      Thread tArray[];
      int numThreads;

      numThreads=Thread.activeCount();
      tArray=new Thread[numThreads];
      Thread.enumerate(tArray);
      for(int i=0;i
      if(tArray[i].getName().startsWith("booThread"))
        tArray[i].stop();
    }
}

  • public void start()  This method gets a new instance of the CorruptedDataExample class and starts 20 threads. Each thread has an individual name that is from "booThread0" to "booThread19". The threads start running in CorruptedDataExample's run method.
  • public void stop()  This method calls stopThreads() in CorruptedDataExample. The class CorruptedDataExample does not guard against corruption of its instance variable num. The class has two methods, which are as follows:
  • public void run()  This method has an infinite loop that shows how data can be corrupted by multiple threads. Twenty threads are executed in this loop at the same time. The loop does the following:
  • Adds 10 then -10 to num 1000 times
  • Sleeps
  • Prints a string that contains num
  • The first step is the cause of the corruption. It serves no purpose other than illustrating how data is corrupted.
  • void stopThreads()  All of the booThreads are stopped in this method. A list of threads is fetched. All of the threads that have names that begin with booThreadare stopped.
The following is the ProblemThreadExample output:
booThread0 sees the number: 0
booThread1 sees the number: 0
booThread2 sees the number: 0
booThread3 sees the number: 0
booThread4 sees the number: 10
booThread6 sees the number: 10
booThread7 sees the number: 10
booThread8 sees the number: 10
booThread9 sees the number: 10
booThread10 sees the number: 0
booThread11 sees the number: 0
booThread12 sees the number: 0
booThread13 sees the number: 0
booThread5 sees the number: 0
booThread14 sees the number: 0
booThread15 sees the number: 0
booThread16 sees the number: 0
booThread17 sees the number: 0
booThread18 sees the number: 0
booThread19 sees the number: 0
booThread0 sees the number: 0
booThread1 sees the number: 0
booThread3 sees the number: 0
booThread4 sees the number: 0
booThread6 sees the number: 0
booThread8 sees the number: 0
booThread9 sees the number: 0
booThread2 sees the number: 0
booThread7 sees the number: 0
booThread10 sees the number: 10
booThread11 sees the number: 0
booThread12 sees the number: -10
booThread13 sees the number: -10
booThread5 sees the number: -10
booThread14 sees the number: -10
booThread16 sees the number: -10
booThread17 sees the number: -10
booThread18 sees the number: -10
booThread19 sees the number: -10

What Goes Wrong?

The first step in the infinite loop would have no ill effect in a single-threaded environment. It simply adds and then subtracts 10 to a variable with the net result being no change. However, in a multithreaded environment, the operations can interfere with each other. You can see one scenario in the following steps in which two threads try to add 10 at the same time.

Step
Thread AThread B
num
1.
Atmp num 
0
2.
Atmp Atmp+10 
0
3.
 Btmp num
0
4.
 Btmp Btmp+10
0
5.
 num Btmp
10
.
..
.
.
..
.
.
..
.
10.
num Atmp 
10

Two num=num+10; operations have been executed, but the value of num has only increased by 10. The problem here is that Thread A was interrupted in the middle of its operation before it could save its results. Threads A and B have added 10 to the same number.
This type of problem is somewhat rare, but should not be ignored. In the previous example, 10 is added and subtracted 1000 times in 20 threads, and the problem still did not occur that often. The bad thing about these types of bugs is that they can be extremely difficult to find. Imagine that num is the index of an array. The problem may not show up until long after num has been corrupted. Generally, these bugs are not reproducible, so they are hard to catch. In some ways the problem is amplified in Java because Java is expected to run on so many platforms. On some systems, num=num+10 may be an atomic operation (cannot be interrupted). In this case, everything works fine. A developer may create an applet on such a system thinking that everything is fine, but it may not work when someone from a different system views the applet. Data corruption can also be more common with other data types. Integer operations are simple compared to many others. Arrays or other data structures can take much longer to process so it is much more likely to be corrupted.

Thread Names and Current Threads

In Listing 16.3, the threads are given names. They are named booThread0 through booThread19 when they are created by the constructor Thread(Runnable,String). The names can be used to identify the threads. The thread names are printed along with num in the run() method of CorruptedDataExample. The Thread methodcurrent.Thread() is called to get a reference to the currently running thread.
The names are also used to stop the threads. A reference is needed for each thread so that stop() can be called to kill it. Thread.enumerate(Thread[]) gets a reference to every thread in this group. Some of these threads are the booThreads, but they may be others. The other threads should not be killed. Before each thread is killed it is checked to see if its name starts with booThread.

Java's synchronized

A way to prevent data from being corrupted by multiple threads is to prevent the interruption of critical regions. Critical regions are places like num=num+10 above, where only one thread should be running at once. Java's synchronized can be used to ensure that only one thread is in a critical region at once. When the thread enters a synchronized code block, it tries to get a lock on that region. While the thread is in the critical region, no other thread can enter the critical region. If a thread tries to enter and the code is already locked, the thread has to wait for the other thread to leave the critical region. Listing 16.3 can be fixed by synchronizing the access to num. The run()method in CorruptedDataExample can be modified to the following:
  public void run()
    {
      int i=0;
      int tmp;       /*new*/
      for(;;)
        {
          for(i=0;i<1000;i++)
            {
                synchronized (this)  /*new*/
                {
                  num=num+10;
                  num=num-10;
                }
            }

          try {Thread.sleep(10000);}
          catch (InterruptedException e ) {}
          synchronized (this)                    /*new*/
          {tmp=num;}                             /*new*/
          System.out.println(Thread.currentThread().getName()+
                     " sees the number: " + tmp); /*new*/
        }
    }
The following lines make up a protected critical region:
            synchronized (this)
        {
          num=num+10;
          num=num-10;
        }
synchronized (this) ensures that only one thread can be in the following code block. The argument this tells the thread to use the lock for this this object.
The variable num is also referenced when the string is printed. The new code is as follows:
      synchronized (this)                                 /*new*/
      {tmp=num;}                                           /*new*/
      System.out.println(currentThread().getName()+
                 " sees the number: " + tmp);     /*new*/
A critical region is used to copy num to temporary storage. The string is then printed using the temporary storage. It would have been possible to synchronize the print line directly, but it would cut performance because the print line does many other things that have nothing to do with referencing num. All the threads waiting to enter the critical region will needlessly wait longer while the print line is executed. Generally, the synchronized blocks should be as small as possible while still protecting the critical region.
You may also think that the other variables in the run method, i and tmp, also need to be synchronized, but it's not necessary. Both i and tmp are method variables so each running thread has its own private copy. There are 20 i's and tmp's but there is only one num.

Synchronizing Threads

Listing 16.4 can be seen in Figure 16.3. This example shows how synchronized can be used to control critical regions. There are two synchronized methods:drawRoundTarget() and drawSquareTarget(). If a thread is in a synchronized method, no other thread can be in any synchronized method that uses the same lock. This example draws only one square or circle at a time. The seven methods of the SynchronizedThreadExample applet are shown after Listing 16.4.

import java.applet.Applet;
import java.awt.*;

public class SynchronizedThreadExample extends Applet
implements Runnable
{
  Image offImage;        /* off screen image      */
  Graphics offGraphics;  /* Graphics for offImage */
  Thread Thr1, Thr2;     /* threads               */

  public void start()
    {
      offImage=createImage(400,300);
      offGraphics=offImage.getGraphics();
      offGraphics.setColor(Color.white);
      offGraphics.setXORMode(Color.blue);
      Thr1=new Thread(this);  Thr1.start();
      Thr2=new Thread(this);  Thr2.start();
    }

  public void stop()
    {
      Thr1.stop();
      Thr2.stop();
    }

  public void paint(Graphics g)
    {
      g.drawImage(offImage,0,0,null);
    }

  public void update(Graphics g)
    {
      paint(g);
    }

  public void run()
    {
      for(;;)
        {
          drawRoundTarget();
          drawSquareTarget();
        }
    }

  synchronized void drawRoundTarget()
    {
      for(int r=0,i=10;i>-20;i-=20)  /* i=(10,-10)                */
        for(r+=i;(r<90)&&(r>0);r+=i) /* r=(10,20...80,80,70...10) */
          {
            offGraphics.fillOval(200-r,150-r,2*r,2*r);
            repaint();
            try {Thread.currentThread().sleep(200);}
            catch (InterruptedException e) {}
          }
    }

  synchronized void drawSquareTarget()
    {
      int i,r;
      for(r=0,i=10;i>-20;i-=20)      /* i=(10,-10)                */
        for(r+=i;(r<90)&&(r>0);r+=i) /* r=(10,20...80,80,70...10) */
          {
            offGraphics.fillRect (200-r,150-r,2*r,2*r);
            repaint();
            try {Thread.currentThread().sleep(250);}
            catch (InterruptedException e) {}
          }
    }
}
The methods of the SynchronizedThreadExample applet are as follows:
  • public void start()/stop()  The threads are started and stopped in these methods.
  • public void paint/update(Graphics)  These methods paint the applet.
  • public void run()  Both threads start executing in this method. It is an infinite loop that draws round and then square targets.
  • synchronized void drawRoundTarget()  This method is synchronized. Only one thread can be inside drawing circles.
  • synchronized void drawSquareTarget()  This method is like drawRoundTarget, but draws squares instead of circles.

Multiple Locks

What if two locks are needed?
The current applet only allows one target to be drawn at a time, be it round or square. Suppose that you want the applet to draw a round and a square target at the same time; you would need two locks for two independent critical regions. The problem is that each object has only one lock. Creating separate classes for drawRoundTarget()and drawSquareTarget() could solve the problem, but it may not be convenient. A better way is to create new objects, and to use their locks to control the methods. This is done by modifying Listing 16.4 as follows:
       .             
       .             
       .             
  Object RoundSync,SquareSync;              /*new*/

  public void start()
    {
       .            .
       .            .
       .            .
      RoundSync=new Object();               /*new*/
      SquareSync=new Object();              /*new*/
      Thr1=new Thread(this);  Thr1.start();
      Thr2=new Thread(this);  Thr2.start();
    }

  void drawRoundTarget()
    {
      synchronized (RoundSync)              /*new*/
        {
          for(int r=0,i=10;i>-20;i-=20)
             .            .
             .            .
             .            .
        }
    }

   void drawSquareTarget()
    {
      synchronized (SquareSync)             /*new*/
        {
          for(r=0,i=10;i>-20;i-=20)
             .            .
             .            .
             .            .
        }
    }
}
Two new Objects are created: RoundSync and SquareSync. The Objects don't actually do anything themselves, but their locks are used when drawing the targets. The instances of Object are obtained in the start method. synchronized (RoundSync) is put in front of the for loops in the drawRoundTarget() method anddrawSquareTarget() is modified similarly. When a thread tries to execute the body of the target drawing methods, it first has to get a lock. drawRoundTarget() gets a lock from the object RoundSync and drawSquareTarget() gets a lock from SquareSync(). After these modifications have been made, the drawRoundTarget() anddrawSquareTargets() methods do not block each other. The applet draws round and square targets at the same time. But it is not able to draw two round targets or two square targets at the same time. Figure 16.4 shows the results of the modifications.
There is one very serious problem with this algorithm. Suppose that each philosopher picks up the right chopstick at the same time. When they try to get the left stick, it won't be there. The neighbor to the left has it. All of the philosophers starve to death with a chopstick in one hand and food on the table. This is known as a deadlock.

Deadlocks

Deadlocks are always a danger in multithreaded environments. A deadlock has occurred because
  • Each thread needed exclusive use of the chopsticks.
  • One thread is not allowed to take a chopstick from its neighbor.
  • Each thread is waiting while holding a chopstick that another thread is waiting for.
All deadlocks in any problem have the same reasons for deadlocking. Instead of waiting for chopsticks, they are waiting for some other resource or resources. If only one of the conditions can be broken, a deadlock will not occur.
The first condition is usually hard to break. In the previous algorithm this could done by allowing philosophers to share a chopstick, but that isn't really possible. Sometimes threads need exclusive use of a resource. Things like the instance of a class or a socket may require that only one thread may use it at once.
The second condition can sometimes be used to avoid deadlocks. If a philosopher was allowed to take a chopstick from its neighbor, there would not be a deadlock. However, if the philosophers keep taking sticks from each other, they may never get a chance to take a bite. Some problems can be solved by allowing resource stealing. It depends on the resource and the problem.
If the deadlock cannot be avoided with the other conditions, it should be avoided by breaking the third condition. The philosophers can avoid a deadlock if there is a special chopstick that they aren't allowed to hold while they are waiting for the second chopstick. They are allowed to eat with the special stick, but they can't just hold it. An algorithm should not be too strict, otherwise the resources may be underused. For example, an algorithm could be made that only allows one philosopher to eat at once. Obviously, there would be no deadlocks, but a philosopher may have to wait longer before it can eat.

Java's wait() and notify()

Java has three wait() and two notify() methods that aid in synchronizing threads. The wait() methods cause the thread to pause in the critical region. While paused, the thread releases its lock. It must get the lock again before it starts executing again. The notify() methods wake up threads that have called wait(). Calling notify() when no wait() has been called has no effect. The methods shown below are in the Object class and can only be called in a synchronized block or method.
  • public final void wait()  This method causes the thread to wait forever until a notify() or notifyAll() is called. The thread releases its lock on the critical regions so that other threads may enter.
  • public final void wait(long m)  This method causes the thread to wait m milliseconds for a notify() or notifyAll() to be called. After the time is expired, the thread tries to resume execution. However, it must first reobtain the lock for the critical region. Another thread may have entered the critical section while the thread was waiting.
  • public final void wait(long m, int n)  This method is similar to the previous one except that it waits for m milliseconds plus n nanoseconds.
  • public final void notify()  This method wakes up a thread that has previously called wait(). The thread that was waiting has to get the lock before it can resume execution. It has to wait until the current thread leaves the critical region. Only one thread that has called wait() is woken up. It is not guaranteed that the first thread that called wait() is the first one woken up.
  • public final void notifyAll()  This method wakes up all the threads that have called wait(). Each waiting thread has to get the lock for the critical region before it resumes. There can still be only one thread running in the critical section at once.

Dining Philosophers Example

The Dining Philosophers applet uses four classes: the ones shown in Listings 16.5, 16.6, 16.7, and 16.8. The first class, DiningPhilosophers, extends the Applet class. The structure of this class is similar to the first example. Philosopher threads are created when the applet is initialized. They are suspended if the user leaves the page and resumed if the user returns. However, unlike the first example, no threads are created in this class. The Dining Philosophers example can be seen in Figure 16.5.
public class DiningPhilosophers extends Applet
{
  final int numPhils=5;

  Image offImage;        /* off screen image      */
  Graphics offGraphics;  /* Graphics for offImage */
  Philosopher Phil[] = new Philosopher[numPhils];
  Chopstick Stick[] = new Chopstick[numPhils];
  ScenePainter painter;

  public void init()
    {
      int i;

      offImage=createImage(400,300);
      offGraphics=offImage.getGraphics();
      painter=new ScenePainter(offGraphics,this,numPhils);

      for(i=0;i
        Stick[i]=new Chopstick (i==0 ? Chopstick.gold :
                                       Chopstick.wood,
                                painter,i);
      for(i=0;i
        Phil[i]= new Philosopher(Stick[i],Stick[(i+1)%numPhils],
                                 painter,i);
     }

  public void start()
    {
      int i;
      for(i=0;i
        Phil[i].resume();
    }

  public void stop()
    {
      int i;
      for(i=0;i
        Phil[i].suspend();
    }

  public void destroy()
    {
      int i;
      for(i=0;i
        {
          Phil[i].resume();
          Phil[i].stop();
        }
    }

  public void paint(Graphics g)
    {
      g.drawImage(offImage,0,0,null);
    }

  public void update(Graphics Dijkstra)
    {
      paint(Dijkstra);
    }
}

The class DiningPhilosophers has six methods and is similar to the InfiniteThreadExample.
  • public void init()  This method initializes the applet. It creates five instances of the classes Chopstick and Philosopher. One of the Chopstick classes is created as a gold chopstick, the rest are wood. Each Philosopher can reach two Chopsticks. On the right is Chopstick i, and on the left is Chopstick i+1 mod 5.
  • public void start()  This method resumes philosopher execution.
  • public void stop()  This method suspends philosopher execution.
  • public void destroy()  This method kills the philosophers.
  • public void paint()/update()  These two methods paint the state of the philosophers.
Each philosopher at the table is its own thread. The thread is created in the class Philosopher by extending Thread. The methods in the class control the philosopher's life of thinking and eating. Initially, the philosopher is thinking. After some time, the philosopher picks up the two chopsticks next to it and starts eating. It calls methods in theChopstick class (see Listing 16.7) to get the chopsticks. The philosopher also paints its state by calling methods in the ScenePainter class (see Listing 16.8).

class Philosopher extends Thread
{
  final int ThinkTime=5000, EatTime=3000;
  Chopstick rightStick,leftStick;
  ScenePainter painter;
  int rightHand,leftHand;
  int myNum;

  public Philosopher(Chopstick right, Chopstick left,
                     ScenePainter p, int n)
    {
      painter=p;
      myNum=n;
      rightStick=right;
      leftStick=left;
      start();
    }

  public void run()
    {
      for(;;)
        {
          think();
          PickUpSticks();
          eat();
          PutDownSticks();
        }
    }

  void think()
    {
      painter.drawThink(myNum);
      try {sleep((int)(ThinkTime*Math.random()));}
      catch (InterruptedException e) {}
      painter.drawThink(myNum);
    }

  void PickUpSticks()
    {
      for(boolean gotem=false;!gotem;)
        {
          painter.drawFirstGrab(myNum);
          rightHand=rightStick.grabStick();

          painter.drawSecondGrab(myNum);
          if(rightHand==Chopstick.gold)
            {
              painter.drawGoldGrab(myNum);

              rightStick.dropStick(rightHand);
              leftHand=leftStick.grabStick();
              if(leftHand==Chopstick.gold)
                {
                  leftStick.dropStick(leftHand);
                  continue;  /* gold stick went around table */
                }
              rightHand=rightStick.grabStick();
              painter.drawGoldGrab(myNum);
            }
          else leftHand=leftStick.grabStick();

          painter.drawSecondGrab(myNum);
          painter.drawFirstGrab(myNum);
          gotem=true;
        }
    }

  void eat()
    {
      painter.drawEat(myNum);
      try {sleep((int)(EatTime*Math.random()));}
      catch (InterruptedException e) {}
      painter.drawEat(myNum);
    }

  void PutDownSticks()
    {/* swap sticks and put them down */
      rightStick.dropStick(leftHand);
      leftStick.dropStick(rightHand);
    }
}

The class Philosopher is used to start the threads for the Dining Philosophers applet. The class has the following five methods:
  • public void run()  This method defines the philosophers actions. Each philosopher thinks, waits to pick up its chopsticks, eats, returns the chopsticks, and repeats the cycle.
  • void think()  This method is where the philosopher thinks. The thinking image is drawn and the philosopher sleeps for a random amount of time.
  • void PickUpSticks()  In this method, the philosopher picks up the chopsticks in a way that is fair and avoids deadlocks.
  • void eat()  This method is where the philosopher eats. The eating image is drawn and the philosopher sleeps for a random amount of time.
  • void PutDownSticks()  In this method, the philosopher returns the sticks. The chopsticks are switched when they are put down so that the gold stick is not always in the same place.
All of the synchronization is done in the Chopstick class. There is one instance of this class for each chopstick on the table. The class is used by the philosophers when they want to pick up or return a chopstick. The three states of the chopstick are represented by the variable stickHoldernoStick means the chopstick is gone, wood means this is the wooden stick, and gold means this is the golden stick. stickHolder is an instance variable. More than one philosopher may be trying to get/drop it at once, so there is a danger of data corruption. The methods of this class are synchronized to ensure that stickHolder does not get corrupted (see Listing 16.7).

class Chopstick extends Object
{
  final static int noStick=0;
  final static int wood=1;
  final static int gold=2;
  ScenePainter painter;
  int stickHolder;
  int myNum;

  public Chopstick(int stick, ScenePainter p, int n)
    {
      painter=p;
      myNum=n;
      dropStick(stick);
    }

  synchronized int grabStick()
    {
      int Thuy;

      if(stickHolder==noStick)
        try {wait();} catch (InterruptedException e) {}
      painter.drawStick(myNum,stickHolder);
      Thuy=stickHolder;
      stickHolder=noStick;
      return Thuy;
    }

  synchronized void dropStick(int stick)
    {
      stickHolder=stick;
      painter.drawStick(myNum,stickHolder);
      notify();
    }
}

The class Chopstick is used to synchronize the threads in the Dining Philosophers applet. The class has the following two methods:
  • synchronized int grabStick()  Philosophers (threads) will come into this method when they attempt to get a stick. If the stick is there, the method gives the stick to the philosopher. If the stick has already been taken by its neighbor, the philosopher waits for the stick to be returned.
  • synchronized void dropStick()  The philosophers use this method to return the sticks. If the other philosopher is waiting for this chopstick, it is woken.
The class ScenePainter is used by the philosophers to paint their state. If a philosopher starts eating, puts down a stick, or does anything else, it calls methods in this class. The states of philosophers (for example, eating or waiting for a stick) are represented by different shapes. When a philosopher starts thinking, it calls drawThink(myNum) to draw the philosopher in its thinking state. Then, when it is done thinking, it calls drawThink(myNum) again to erase the philosopher. All of the methods in this class are called in pairs. The first call draws a state and the second erases it.

class ScenePainter extends Object
{
  int sX1[], sY1[], sX2[], sY2[], pX[], pY[];
  final int xOffset=150, yOffset=150, Tscale=70;
  final int rg=2, rEating=15, rThinking=8, rFirst=7;
  final int rSecond=3, rGold=5;
  Graphics G;
  Component C;

  public ScenePainter(Graphics g, Component c, int numPhils)
    {
      int i;
      pX  = new int[numPhils]; pY  = new int[numPhils];
      sX1 = new int[numPhils]; sY1 = new int[numPhils];
      sX2 = new int[numPhils]; sY2 = new int[numPhils];
      double arc=Math.PI/numPhils;

      G=g; C=c;

      for(i=0;i
        {
          pX[i]= (int)(xOffset+   Tscale*Math.cos(i*2*arc));
          pY[i]= (int)(yOffset+   Tscale*Math.sin(i*2*arc));
          sX1[i]=(int)(xOffset+   Tscale*Math.cos(i*2*arc+arc));
          sY1[i]=(int)(yOffset+   Tscale*Math.sin(i*2*arc+arc));
          sX2[i]=(int)(xOffset+.7*Tscale*Math.cos(i*2*arc+arc));
          sY2[i]=(int)(yOffset+.7*Tscale*Math. sin(i*2*arc+arc));
        }
      G.setColor(Color.white);
      G.setXORMode(Color.blue);
    }

  void drawStick(int num, int stick)
    {
      G.drawLine(sX1[num],sY1[num],sX2[num],sY2[num]);
      if(stick==Chopstick.gold)
        G.fillOval(sX1[num]-rg,sY1[num]-rg,rg+rg,rg+rg);
      C.repaint();
    }

  void drawEat(int num)
    {
      fillCircle(num,rEating);
    }

  void drawThink(int num)
    {
      fillCircle(num,rThinking);
    }

  void drawFirstGrab(int num)
    {
      fillSquare(num,rFirst);
    }

The class ScenePainter is used to draw the state of all the philosophers in the Dining Philosophers applet.
  • public ScenePainter(Graphics, Component, int)  The constructor for this class calculates the position of each philosopher and chopstick.
  • void drawStick(num, stick)  This method draws/erases the chopstick specified by num. A stick is represented by drawing a line between the two philosophers that may use it. If stick is the gold chopstick, a small circle is added to the end of the chopstick.
  • void drawEat(num)  This method draws/erases the philosopher specified by num in its eating state. A large circle represents the eating state.
  • void drawThink(num)  This method draws/erases a small circle that represents a philosopher in its thinking state.
  • void drawFirstGrab(num)  This method draws/erases a small square. The method is called when the philosopher tries to grab the first stick.
  • void drawSecondGrab(num)  This method erases/draws a smaller square inside the square drawn by drawFirstGrab(num). The method is called after the philosopher already has one chopstick and is trying to grab the second. A philosopher in this state is represented by a small hollow square.
  • void drawGoldGrab(num)  This method is called if the first stick the philosopher picked up is the gold stick. It is only called after both drawFirstGrab(num) anddrawSecondGrab(num) have been called. A philosopher in this state is represented by a small hollow square with a tiny circle in the middle.

Read more...

  © Blogger templates ProBlogger Template by Ourblogtemplates.com 2008

Back to TOP