JDC Tech Tips Vol. 2 No. 2

JDCTechTips@sun.com
Thu, 17 Sep 1998 06:16:09 GMT

-WELCOME- to the Java(sm) Developer Connection(sm) Tech Tips. This issue
covers using synchronized statements and finally clauses. The JDC Team-

J D C T E C H T I P S

TIPS, TECHNIQUES, AND SAMPLE CODE
* Using Synchronized Statements
* Finally Clauses

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
T I P S , T E C H N I Q U E S , A N D S A M P L E C O D E

USING SYNCHRONIZED STATEMENTS. Suppose that you're experimenting with
Java(tm) language threads, and you write a simple program such as the
following:

public class sync extends Thread {
static int n = 1;

public void run()
{
for (int i = 1; i <= 10; i++) {
System.out.println(n);
n++;
}
}

public static void main(String args[])
{
Thread thr1 = new sync();
Thread thr2 = new sync();

thr1.start();
thr2.start();
}
}

When you run this program, you get output like the following:

1
2
3
4
5
6
7
8
9
9
10
11
13
14
15
16
17
18
19
20

Two instances of "9" occur, and none of "12" occur. Your results may vary
from this output, which is the whole point of this example.

Such results are "impossible," because "n" is incremented immediately after
printing its value, isn't it? How can the same value be printed twice, or
another value omitted altogether?

The reason that this type of output can occur has to do with the nature of
thread programming. In the following sequence:

System.out.println(n);
n++;

with two threads executing, nothing says that these two statements must be
executed atomically (that is, without any interruption between statements)
within one thread, without the other thread gaining control. For example,
a sequence such as the following:

System.out.println(n);
System.out.println(n);
n++;
n++;

is quite possible, leading to the results show above. This problem is
basic to multithreaded programming, system-level programming in operating
systems, and so on.

To solve this problem, the example can be rewritten as follows:

static Object critsect = new Object();

public void run()
{
for (int i = 1; i <= 10; i++) {
synchronized (critsect) {
System.out.println(n);
n++;
}
}
}

This technique typically goes by the name of "critical sections," that is,
regions of a program that can be safely executed by only one thread at a
time. In this example, a class variable named "critsect" is set up, and
used together with the "synchronized" statement to lock the region of code
within the { } against simultaneous access. This procedure guarantees that
the I/O statement and the following increment are performed atomically for
a particular thread, before another thread is allowed to perform the same
operations.

Also, another type of synchronization is the synchronized instance method.
In this method, the whole method is protected against simultaneous access,
based on obtaining a lock on the "this" reference to the particular class
instance being operated upon by the method.

FINALLY CLAUSES. You may have seen the try...catch statement used in
exception handling. With such a statement, a block of code is tried
(executed), and any exceptions thrown as a result are directed to the
various catch clauses. This type of statement also supports the use of a
"finally" clause, which is a block of code that is executed whether or not
the try block completes normally, results in an exception, or exits the
containing method. A finally clause is therefore useful as a cleanup
mechanism, and try...finally can be used without catch, and without
specifically using exceptions.

To see how all this works, consider the construction of a simple method,
one that copies one file to another. The method should accept two file
names, and propagate out any IOException that occurs (rather than trapping
it internally). Here is some code that implements this idea, along with a
driver program:

import java.io.*;

public class FileCopy {
public static void copy(String from, String to)
throws IOException
{
FileInputStream fis = null;
FileOutputStream fos = null;

try {
fis = new FileInputStream(from);
fos = new FileOutputStream(to);

final int BUFSIZ = 1024;
byte buf[] = new byte[BUFSIZ];
int len = 0;

while ((len = fis.read(buf)) > 0)
fos.write(buf, 0, len);
}
finally {
if (fis != null)
fis.close();
if (fos != null)
fos.close();
}
}

public static void main(String args[])
{
// do many invalid copy() operations

for (int i = 1; i <= 5000; i++) {
try {
copy("xxx.txt", ".");
}
catch (IOException e) {
}
}

// do a valid operation

try {
copy("xxx.txt", "yyy.txt");
}
catch (IOException e) {
System.err.println(e);
}
}
}

The driver exercises a particular aspect of this programming problem, that
of gracefully handling repeated errors. In this example, the error is an
invalid output file for the copy (an attempt is made to copy to ".").

The copy method does not require a try...catch statement, because it
propagates out the exception, and declares it in a throws clause. But what
happens if copy is repeatedly called, with an invalid output file argument,
after the FileInputStream for the input file has been successfully
established? What can happen is a resource leak, with operating system
file descriptors being allocated and eventually depleted. This situation
is not the same as a memory leak, but instead simply reflects a failure to
call close on the FileInputStream. Garbage collection will result in the
finalize method being called for FileInputStream, and it calls close, but
there's no guarantee that garbage collection will be invoked in a timely
way. So the second copy invocation in the example above, one that is
supplied valid arguments, fails without the try...finally.

To fix this problem, a try...finally statement is used. No matter what
happens in copy, the finally clause will be invoked, and it will close any
open input stream. In this way, a finally clause can be used as a general
cleanup mechanism.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
-- NOTE --

The names on the JDC mailing list are used for internal Sun
Microsystems(tm) purposes only. To remove your name from the list, see
Subscribe/Unsubscribe below.

-- FEEDBACK --

Comments? Send your feedback on the JDC Tech Tips to:

JDCTechTips@Sun.com

-- SUBSCRIBE/UNSUBSCRIBE --

The JDC Tech Tips are sent to you because you elected to subscribe when you
registered as a JDC member. To unsubscribe from JDC Email, go to the
following address and enter the email address you wish to remove from the
mailing list:

http://developer.java.sun.com/unsubscribe.html

-- ARCHIVES --

You'll find the JDC Tech Tips archives at:

http://developer.javasoft.com/developer/javaInDepth/TechTips/index.html

-- COPYRIGHT --

Copyright 1998 Sun Microsystems, Inc. All rights reserved.
901 San Antonio Road, Palo Alto, California 94303 USA.

This document is protected by copyright. For more information, see:

http://developer.javasoft.com/developer/copyright.html

The JDC Tech Tips are written by Glen McCluskey.

JDC Tech Tips Vol. 2 No. 2
September 15, 1998