Given by Nancy J. McCracken,Geoffrey C. Fox, Tom Scavo at CEWES Tutorial, CPS606, JSU Class CSC499 on July 22-25 1997, Fall 97. Foils prepared 17 Nov 97
Outside Index
Summary of Material
In Part 1 2 and 3 of the Tutorial We Covered:
|
In This Part of the Java Tutorial We Cover: |
Threads in Detail |
Useful Java Classes
|
Networking and I/O |
Futures and HPCC Implications |
Outside Index Summary of Material
Instructors: Geoffrey Fox, |
Nancy McCracken, |
Tom Scavo |
Syracuse University |
111 College Place |
Syracuse |
New York 13244-4100 |
Java is remarkable for threads being built into the language |
Threads are "light-weight" processes (unlike UNIX processes), which communicate by a combination of shared memory and message passing
|
Java threads are limited and for those coming from an HPCC background, we note Java threads have no immediate support for some key parallel computing concepts (see work of Chandy at Caltech) such as distributed memory (threads running in separate operating system instances) |
Java threads are based on a locking mechanism using monitors for synchronization, introduced by Hoare in 1974 |
One can implement threads in two ways:
|
A class that implements the Runnable interface (including the Thread class itself) must implement the run() method containing the "body" of the thread. |
The Runnable interface makes it possible for an applet to utilize threads. (Remember, an applet extends the Applet class and so cannot multiply inherit from the Thread class.) |
One way to create threads is to write a separate class that subclasses the Thread class.
|
Instances of this subclass are instantiated like this: |
MyThread mt = new MyThread(...); |
Thread control:
|
Threads are implemented by a scheduler in Java, which asks the local operating system to run threads in the "runnable" state. |
Typically, the OS runs each thread in turn for a "time slice". However, some operating systems (early versions of Solaris, e.g.) run a thread to completion unless another thread of higher priority preempts the running thread. |
Example of two threads running: Each thread gives up execution voluntarily (by executing yield(), suspend(), etc.) or because its time slice has ended: |
A thread is always in one of the five states shown in this diagram, which includes the most common methods for changing state: |
A thread must move out of a blocked state into the runnable state using the opposite of whatever put it into the blocked state:
|
Every thread has a priority, which can be set by the user with setPriority( int ) using constants MIN_PRIORITY (1), MAX_PRIORITY(10), or NORM_PRIORITY. |
Whenever the thread scheduler picks a thread to run, it picks the highest priority thread that is currently runnable. (If there is more than one thread with the same priority, each thread gets a turn in some order.) |
The user can also create a ThreadGroup to organize a large number of threads by functionality. The methods stop(), suspend(), and resume() can be used on a ThreadGroup. |
In Java, two threads can communicate by accessing a shared variable (shared-memory model). |
If two threads can both modify an object, that is, they can both execute a method that assigns to a shared variable, then the modifications must be synchronized. |
This is easy - just declare the method to be synchronized! |
This means that Java will ensure that only one thread executes the method at a time. |
Suppose more than one thread can access an account: |
public class Account |
{ int bankBalance; ...
|
An object that can block threads and notify them when it is available is called a monitor. A monitor is associated with an instance of the class; it has a lock and a queue. |
If a class has one or more synchronized methods, each object (instance) of the class has a monitor. The queue holds all threads waiting to execute a synchronized method.
|
If a thread must wait for the state of an object to change, it should call wait() inside a synchronized method. |
void wait() |
void wait( int timeout )
|
void notify() |
void notifyAll()
|
notify() notifies the thread associated with the given synchronization object that has been waiting the longest time |
notifyAll() notifies all threads associated with the given object and is therefore safer than notify() |
One can mark a variable as "threadsafe" to inform the compiler that only one thread will be modifying this variable. |
Suppose that several threads are updating a bank balance (i.e., several threads can access one instance of class Account below). Then a thread that finds insufficient funds to debit an account can wait until another thread adds to the account: |
public class Account |
{ int bankBalance; ...
|
Public class Object is the root of the class hierarchy. Every Java class has Object as its ultimate parent and so any object (object with a small "o" is any instance of a class) can use methods of Object. |
Methods of Object include:
|
Suppose we have an object called obj. We get the class of obj by: |
Class class = obj.getClass(); |
and its name by: |
String name = class.getName(); |
One can also use instanceof in following fashion: |
"foo" instanceof String |
evaluates to true, but |
( new mPoint(x,y) ) instanceof String |
evaluates to false. |
Primitive types such as int, char, float, etc. are NOT classes. Thus one cannot use methods such as |
int var; |
var.toString(); |
ALL primitive types have associated wrappers: |
Character myChar = new Character( 'A' ); |
The Character class has methods such as: |
if ( myChar.equals( ch ) ) ... |
System.out.print( myChar.toString() ); |
There are also many static (class) methods: |
ch = Character.toLowerCase( myChar ); |
if ( Character.isUpperCase( myChar ) ) ... |
The methods in a wrapper class are also useful to convert types, such as a String to a Double. |
This class provides standard mathematical functions, using types int, long, float and double. |
It is a static class, meaning that you only use the methods and never create "Math objects". |
The methods include
|
This class provides an implementation of "date" structures. Date has methods to create and compare dates, obtain and set the time, and convert dates to strings. |
The Date constructor creates today's date: |
Date today = new Date(); |
In Java 1.1, most Date methods have been deprecated in favor of the Calendar class: |
Calendar date1 = Calendar.getInstance(); |
date1.set( 999, 12, 31 ); /* Dec. 31, 999 */ |
Calendar date2 = Calendar.getInstance(); |
date2.set( 1996, 12, 31, 23, 59, 59 )
|
Strings are fixed-length collections of Unicode characters. |
Usually a string is created from a string literal or by using the constructor on an array of characters: |
String greeting = "Hello"; |
or |
char[] bunch = {'H', 'e', 'l', 'l', 'o'}; |
String greeting = new String( bunch ); |
Once created, individual characters of a string cannot be changed in place. The following example uses String methods to create a new string: |
String test = "Chicken soup with rice"; |
int n = test.indexOf( 'w' ); |
String newtest = test.substring(1,n-1) +
|
String comparison is done with the methods equals() and equalsIgnoreCase(). Note that == tests if two strings are the same string instance, while equals() tests if two distinct strings have the same characters. |
Other methods include length(), charAt( int ) and toLowerCase(). |
The StringBuffer class has mutable strings, but with a fixed maximum size. Methods such as append(...) automatically extend the length of the string. |
This class returns an object of class String that reverses order of characters in its argument: |
class ReverseString {
|
} |
Or more simply: |
StringBuffer sb = new StringBuffer( s ); |
return sb.reverse().toString(); |
In Java, while you can give the size of an array at run time, you cannot dynamically change the size of an array during the computation. The vector class provides a data structure with just this property, but the restriction is that all of the elements must be of type Object.
|
A vector is created with an "initial capacity" and a "capacity increment". (The default is an initial capacity of 10 and an increment that doubles each time.) As you add elements, if the initial capacity is exceeded, then more memory is automatically allocated in the size of the capacity increment. |
Vector shoes = new Vector(); |
Vector orders = new Vector(100, 10); |
Elements are created with the addElement( ... ) method: |
Order missouri = new Order(); |
orders.addElement( missouri ); |
The object missouri of type Order is automatically converted to an Object and added to Vector instance orders defined on the previous foil. |
There are methods for indexing vectors. Like arrays, the indexing is zero-based. |
x = (Typeofx) v.elementAt(i); |
v.setElementAt( x, i ); |
The length of the Vector may also be obtained: |
int size = v.size; |
This class is similar to a Perl associative array (or hash). It can store a set of key-value pairs, neither of which can be null. |
Hashtable staff = new Hashtable(); |
Employee harry = new Employee("Harry Hacker"); |
staff.put( "987-98-9996", harry ); |
Values are retrieved by indexing with a key. Like Vectors, Hashtables only store objects of type Object, so you must cast the result: |
steve = (Employee) staff.get("149-26-7355"); |
If there is no entry, a null value is returned. |
Performance of the Hashtable can be affected by giving an initialCapacity and a loadFactor for reallocation. |
A stream is a sequence of bytes or characters. |
Stream sources and sinks include:
|
That is, all types of streams are treated similarly. |
The most basic byte streams are InputStream and OutputStream. These classes have methods that can read or write a byte from or to a stream:
|
All of the above methods throw a possible IOException. |
The read() and write( int ) methods "block" during transfer. |
The System class in java.lang provides the "standard" IO streams System.in, System.out, and System.err. |
System.in is an instance of InputStream. |
System.out and System.err are instances of PrintStream. |
PrintStream is a subclass of FilterOutputStream, which itself is a subclass of OutputStream. (See next foil.) PrintStream objects should not be instantiated; use other subclasses of FilterOutputStream for byte streams or PrintWriter objects for character streams. |
PrintStream and PrintWriter define methods print(...) and println(...), which output any primitive type: |
System.out.println( "Enter character: " ); |
int ch = System.in.read(); |
System.out.println( (char) ch ); |
The subclasses of OutputStream offer additional methods that write a byte stream in a more structured way or provide other functionality. |
For example, to open a byte stream to an output file, use: |
FileOutputStream s = new FileOutputStream("/usr/gcf/file"); |
DataOutputStream and BufferedOutputStream are two important FilterOutputStreams. |
To open a data stream to an output file, use: |
DataOutputStream out =
|
where filename is a filename string. |
Note that DataOutputStream has methods to write any primitive type. |
To open a buffered output stream, use: |
BufferedOutputStream out =
|
Only bytes may be written to a BufferedOutputStream. |
The subclasses of InputStream are analogous to those of OutputStream. |
For example, to open a byte stream to an input file, use: |
FileInputStream s = new FileInputStream("/usr/gcf/file"); |
Subclasses of FilterInputStream are used to add value to a raw InputStream. You can define your own filters but useful ones are already provided in java.io:
|
These streams may be "chained" for added functionality: |
DataInputStream in =
|
or |
BufferedInputStream in =
|
where file is a filename string. |
The constructor of SequenceInputStream takes a pair of InputStreams and concatenates them together: |
SequenceInputStream in =
|
Alternatively, SequenceInputStream takes a Java Enumeration type: |
SequenceInputStream in =
|
where args is an array of command-line arguments and FileListEnumerator is a class that implements the Enumeration interface. |
The RandomAccessFile class offers all the functionality of DataInputStream and DataOutputStream combined, plus additional capabilities. |
To open a random access file for reading, use: |
RandomAccessFile in =
|
Such a file may be accessed sequentially with |
in.readLine(); |
or randomly by repositioning the file pointer: |
in.seek( offset ); |
where offset is a byte offset into the random file. (Use "rw" for read/write access.) |
Random access files have no inherent structure; the structure must be imposed by the programmer. |
Java 1.1 introduced Reader and Writer classes for character streams, which are used to read/write text files. |
To construct a character output stream, for example: |
PrintWriter out =
|
The OutputStreamWriter constructor takes a byte stream and converts it to a character stream. As a shortcut, use |
PrintWriter out =
|
where FileWriter is a subclass of OutputStreamWriter. |
For buffered text output, use the character stream: |
BufferedWriter out =
|
Similarly, for buffered text input, use: |
BufferedReader in =
|
Optionally use the subclasses FileWriter and FileReader for brevity (as in the previous foil). |
Note that the BufferedReader class has a handy readLine() method for sequential text input. |
The buffered output construct in the previous foil is of limited use since BufferedWriter has so few output methods. Instead, use the "monster" chain: |
PrintWriter out =
|
which can be shortened somewhat by using FileWriter as shown earlier. |
The PrintWriter class defines print(...) and println(...) methods for all primitive types, which unlike other Reader/Writer classes never throw exceptions. |
The File class defines methods and variables that provide access to the underlying file system in a machine-independent way. |
For example, there are methods getParent() and getPath(), as well as boolean methods isDirectory() and isFile(), plus many more. |
A very handy method is the list() method, which returns a string array of directory contents: |
File dir = new File( "/tmp" ); |
if ( dir.exists() && dir.isDirectory() )
|
Instances of class File may be used in lieu of filename strings in InputStream constructors. |
Classes ObjectOutputStream and ObjectInputStream in package java.io implement object serialization. |
Java objects are serialized with writeObject() and deserialized with readObject(). For example: |
Vector lines = new Vector( 256 ); ... |
try {
|
} catch ( IOException e ) { } |
Only objects of classes that implement Serializable (or Externalizable) can be serialized. (The Serializable interface defines no methods.) |
Object variables not to be serialized are called transient. |
This area will evolve rapidly as existing I/O systems get linked to Java with special classes such as those needed to link MPI (HPCC Message Passing) or Nexus (Well known distributed memory thread package) |
One can establish Web Connection with URL class and access documents there |
One can set up a more general URLConnection for more general input/output operations through Web(HTTP) protocol |
One can set up a Socket link which is permanent rather than on again off again Web client-server interaction |
One can send messages and so transfer information between different Applets |
One aspect of Java security is language restrictions designed not to let a Java applet or application access memory on the machine outside of its own space. |
Applets have additional restrictions:
|
As of summer 1997 no known applets have seriously broken security to steal client information or trash the local disk. Exceptions:
|
This table shows what Java programs can do in the following four cases:
|
JA - Java application (not an applet)
|
read local file no no yes yes |
write local file no no yes yes |
get file information no no yes yes |
delete file no no no yes |
run another program no no yes yes |
read the user.name proper no yes yes yes |
connect to network port on server yes yes yes yes |
connect to network port on other host no yes yes yes |
load Java library no yes yes yes |
call exit no no yes yes |
create a pop-up window with warning yes yes yes |
The class java.net.URL has methods to create and manipulate URL objects: |
String urlStr = "http://www.npac.syr.edu/"; |
try {
|
} catch ( MalformedURLException e ) {
|
} |
The document at this URL may be displayed in the current browser window with: |
getAppletContext().showDocument( url ); |
Another version of showDocument(...) permits the document to be loaded into an arbitrary HTML frame. |
To read a file on the server, first instantiate a URL object: |
try {
|
} catch ( MalformedURLException e ) {
|
} |
where getCodeBase() returns the applet path. (Another method getDocumentBase() returns the path of the corresponding HTML document.) |
Next, open a stream to this URL: |
BufferedReader in =
|
The resulting character stream is treated like any other. |
A class called java.net.URLConnection provides another approach to URLs.
|
A method URL.openConnection() returns an instance of class URLConnection: |
URLConnection conn = url.openConnection(); |
conn.connect(); // open a connection |
Now use this URLConnection object to open a stream: |
BufferedReader in =
|
Note that one can connect not just to HTML files but also to CGI scripts and other web documents. |
A Java applet or application can open a socket to a Java server application through the Socket class.
|
or Socket t = new Socket(this.getCodeBase().getHost(), port) |
The latter form is useful for applets as the applet can only use a socket to a server application running on the web host machine that the applet code was downloaded from. |
Sockets have methods getInputStream and getOutputStream which can be used for communication. Note that both communication directions are possible at the same time on a socket. |
A Java application can open a single Socket on a port and open an InputStream and OutputStream just like the client, but in general, a server will want to be able to open multiple sockets to establish communication with a number of clients at the same time. |
The ServerSocket class allows multiple sockets:
|
Then the Java server application can use the accept method to open a socket for each client which tries to open a socket on the port:
|
Typically, a server main program would be waiting to accept a client. When a client tries to establish a socket, the accept proceeds. Then the server would spawn a thread to handle that one socket, opening input and output streams as necessary and communicating with the client. |
Although an applet can read files from a web server machine, as in, for example, establishing a URL connection and doing getInputStream(). Applets are not allowed to directly write to files on the web server machine by doing getOutputStream(). (Java applications can do this!) |
The applet can establish a socket the URL of the web server. Then it uses an OutputStream on the socket to send a message formatted in MIME, just as if it were from the browser. The header of this message requests the web server to run a CGI script. In addition, the applet can send data in the body of the message for the CGI script to read. And the CGI script can send back a MIME message to the applet. |
One can essentially augment supplied Java Runtime by supplying your own C or other code implementing a particular functionality in a higher performance mode |
This of course generates machine dependence and should only be used if really needed |
First for classes you wish to implement in native fashion, put in your java code lines like: |
public native mustgofast(arguments); // default functions |
static { System.loadLibrary("faststuffhere"); } // static and in class and so runs only ONCE! and loads the native code |
Advantages
|
Disadvantages
|
So usually user defined native method can not be used remotely in Applet.
|
The activities that has gone into defining High Performance Fortran (HPF) and HPC++ can be reexamined for Java which is quite well suited for parallelism as
|
Interesting Compiler challenge independent of parallelism, is to produce efficient code from bytecodes. Here a technique called "just in time" compilation does compilation at runtime and can increase performance of Java code to within a factor of 4 (today) to 2 (tomorrow) performance of C |
Surely many will produce Fortran C PASCAL to Java translators so you can webify your existing applications
|
Current runtime (java Threads) assume shared memory but there are interesting possible distributed memory and distributed shared memory implementations |
One can imagine a data-parallel interface where methods would implement array and other data parallel operations with distributions specified by annotations as in HPF
|
Important implication for parallel CORBA using Java classes to interface with CORBA |
Java based servers will allow data and task parallel Java to implement World Wide compute webs |
see http://www.npac.syr.edu/projects/javaforcse |