Valid XHTML 1.0!
nerius.png  

Rambetter's Java Tricks


I started using Java in 1998, right around the time when Java 1.1 was released. I have been using Java professionally and personally since then. I've decided to put together a webpage showcasing some of the more subtle things that I've learned about Java in my years of programming. This page is very much a work in progress; I will add examples as I think of them. If you see mistakes, or if you'd like to see some other specific examples added to this webpage, please email me at nlandys@gmail.com .

This page is not an introduction to Java. It's not for beginners who don't know how to compile or run programs. If you're able to write, compile, and run a "Hello World!" program in Java, then you may be able to understand some of the material covered here.




Synchronized Methods, Uses and Pitfalls

The synchronized keyword is used to limit one thread at a time to critical sections of code. For example, let's say we're counting the number of times something happens, and we're counting in a multi-threaded environment. We might write a class such as the following:

public class Counter {

  private long count = 0;
  private final Object lock = new Object();

  public void incrementCount() {
    synchronized (lock) {
      count++;
    }
  }

  public long getCount() {
    synchronized (lock) {
      return count;
    }
  }

}

The expression count++ above is not atomic (not even for int primitives). If two threads are executing this line of code concurrently, there will be unpredictable results.

You may be wondering why I need a synchronized for the method getCount(). The reason for this is that assignment to long values is not guaranteed to be atomic in Java. Had we used int instead of long for the count member variable, we could have omitted the synchronized block on getCount().

Programmers who don't pay extreme attention to detail may be inclined to rewrite the Counter class like so:

public class Counter {

  private long count = 0;

  public synchronized void incrementCount() {
    count++;
  }

  public synchronized long getCount() {
    return count;
  }

}

The above is equivalent to the following:

public class Counter {

  private long count = 0;

  public void incrementCount() {
    synchronized (this) { count++; }
  }

  public long getCount() {
    synchronized (this) { return count; }
  }

}

The problem with this approach is that we are synchronizing around an object that is publicly visible, namely the instance of Counter. It is best practice to synchronize around objects that are invisible to people who will be using your code. Our original example defined a private member called lock, and nobody outside of the Counter class will be able to see that object.

If other code sees the object you're synchronizing around, it will be able to syncrhonize around it as well. This may lead to deadlocks. Consider the following main() method:

public class CounterMain {

  public static void main(String[] args) throws InterruptedException {
    final Counter counter = new Counter();
    Runnable runnable = new Runnable() {
        public void run() {
          while (true) {
            for (int i = 0; i < 4000000; i++) {} // Waste CPU cycles.
            counter.incrementCount();
            System.out.print(".");
          }
        } };
    (new Thread(runnable)).start();
    Thread.sleep(1000); // Let the thread start and waste CPU for 1 second.
    synchronized (counter) {
      Thread.sleep(10000); // Hold the lock on counter for 10 seconds.
    }
  }

}

Try running this code with both versions of Counter. You'll notice that the version of Counter that synchronizes around this will lock for 10 seconds. The thread that is calling incrementCount() will block, waiting for the Counter instance to be released. In the case where a private lock is used, there is no way for external code to cause incrementCount() to block. Therefore, the use of a private lock is always preferable, unless the lock you are using to control thread access needs to be publicly accessible. Once your software becomes really complex with multiple threads locking on multiple objects in sequence, this sort of consideration will become very important.



Multiple Return Types, Pass By Reference

A method has at most one return type. You can't return more than one thing from a method. Well that's sort of true. You could define a custom class where each instance of that class contains a few members which are supposed to be your return arguments. Or, equally sloppy would be just to return an array of objects like so:

public class CountryAndElevation {

  // Returns an array of length 2.
  // Index 0 of return array is a String which is a 2 character country code.
  // Index 1 of return array is an Integer which is an elevation (in meters).
  // Returns null if we're underwater (not in a country).
  public static Object[] getCountryAndElevation(float latitude,
                                                float longitude) {
    if (latitude == 42.27 && longitude == -83.73) { // Ann Arbor, MI
      return new Object[] { "US", 256 };
    }
    return null;
  }

}

One thing that Java lacks is a concept of "pass by reference" in functions. In C, for example, it is possible to declare a function which takes an integer passed by reference as an argument; this essentially means that the address of the memory location of an integer is passed to the function, and the function will modify the value at that address. The address could point to something on the stack or to something on the heap. This is a very convenient feature that is missing from Java (for good reasons, I guess).

In Java, when you pass a primitive such as int or float to a method, you have no choice but to have that primitive value copied to a new location higher in the stack that the new function will read from. When this function makes a modification to that copied primitive value, it does modify the memory for that value, but only at the location on the stack that is specific to the scope of that function, not the calling function. In some languages this is called "pass by value".

With any data type that inherits from Object, things are slighly different, but almost the same. Memory that is used by an object is always on the heap (there are no objects on the stack in Java, only the pointer to an object can reside on the stack). When you pass an Object to a method as an argument, it's just an address for the object that is passed to the method. The address to the object is copied to a higher location on the stack specific to the method that is about to be called.

The closest, in my opinion, that you can come to passing by reference in a function is to use the "array trick", which is essentially like dereferencing an object or primitive:

public class PassByReference {

  public static void main(String[] args) {
    int[] intReference = new int[1];
    String[] stringReference = new String[1];
    passByReferenceFunction(intReference, stringReference);
    System.out.println("return values: " + intReference[0] +
                       " and " + stringReference[0]);
  }

  private static void passByReferenceFunction(int[] intByReference,
                                              String[] stringByReference) {
    intByReference[0] = 5;
    stringByReference[0] = "hello";
  }

}

The difference between this way and the "real" way in C is that in the Java example, you are allocating two arrays of length 1 on the heap, which is slightly inefficient. These arrays of length 1 are essentially what a pointer would be in a language such as C.



Networking with UDP Protocol

Writing code that communicates over the network is enormously useful. The common network protocols that Java supports well are UDP and TCP.

UDP is a suitable choice as a network transport machanism when reliability is not of the utmost importance and when simplicity and performance are a primary concern. UDP works by sending discrete packets, whereas TCP abstracts the packet sending to a stream-based concept. There is a larger overhead when using TCP, and code using TCP tends to be more complicated.

Here I have some minimalistic examples that use UDP packets. The client sends the message "Hello, server!" to a server process, and that server responds with "Hi client, love you too!". Both of these messages are printed to standard out. Here is the client code:

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPClient {

  private final static String SERVER_HOST = "localhost"; // "127.0.0.1"
  private final static int SERVER_PORT = 31337;
  private final static int BUFF_SIZE = 64; // not less than MESSAGE.length()
  private final static String MESSAGE = "Hello, server!";

  public static void main(String[] args) throws java.io.IOException {
    InetAddress serverAddr = InetAddress.getByName(SERVER_HOST);
    DatagramSocket udpSocket = new DatagramSocket();
    udpSocket.connect(serverAddr, SERVER_PORT);
    DatagramPacket packet = new DatagramPacket(new byte[BUFF_SIZE], BUFF_SIZE);
    System.arraycopy(MESSAGE.getBytes("US-ASCII"), 0,
                     packet.getData(), 0, MESSAGE.length());
    packet.setLength(MESSAGE.length());
    udpSocket.send(packet);
    packet.setLength(BUFF_SIZE);
    udpSocket.receive(packet);
    System.out.write(packet.getData(), 0, packet.getLength());
    System.out.println();
    udpSocket.close();
  }

}

Here is the server code:

import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UDPServer {

  private final static int LISTEN_PORT = 31337;
  private final static int BUFF_SIZE = 64; // not less than MESSAGE.length()
  private final static String MESSAGE = "Hi client, love you too!";

  public static void main(String[] args) throws java.io.IOException {
    DatagramSocket udpSocket = new DatagramSocket(LISTEN_PORT);
    DatagramPacket packet = new DatagramPacket(new byte[BUFF_SIZE], BUFF_SIZE);
    udpSocket.receive(packet);
    System.out.write(packet.getData(), 0, packet.getLength());
    System.out.println();
    System.arraycopy(MESSAGE.getBytes("US-ASCII"), 0,
                     packet.getData(), 0, MESSAGE.length());
    packet.setLength(MESSAGE.length());
    udpSocket.send(packet);
    udpSocket.close();
  }

}

To run these sample programs, run the server program first. While it is running, run the client program on the same host. They will communicate with each other and then exit.



Networking with TCP Protocol

TCP is a stream-based protocol with the assurance that correct data is delivered in the correct order. (Of course, if the network is down, data will not be delivered, and an exception in the code will be raised.

Using TCP in code is somewhat more complex, as you will see below. With TCP, you really need a protocol for the data which will let you know when the end of a request is reached (on the server side) and when the end of a response is reached (on the client side).

Here is a simple client/server program making use of the TCP protocol. The client:

import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

public class TCPClient {

  private final static String SERVER_HOST = "localhost"; // "127.0.0.1"
  private final static int SERVER_PORT = 31338;
  private final static String MESSAGE = "Are you a happy camper?\0";

  public static void main(String[] args) throws java.io.IOException {
    InetAddress serverAddr = InetAddress.getByName(SERVER_HOST);
    Socket tcpSocket = new Socket(serverAddr, SERVER_PORT);
    OutputStream sockOut = tcpSocket.getOutputStream();
    sockOut.write(MESSAGE.getBytes("US-ASCII"));
    sockOut.flush();
    InputStream sockIn = tcpSocket.getInputStream();
    int oneByte;
    while ((oneByte = sockIn.read()) > 0) { // '\0' will mean end of response.
      System.out.write(oneByte);
    }
    System.out.println();
    tcpSocket.close();
  }

}

The server:

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer {

  private final static int LISTEN_PORT = 31338;
  private final static String MESSAGE = "Yes I am camping in my tent.\0";

  public static void main(String[] args) throws java.io.IOException {
    ServerSocket serverSocket = new ServerSocket(LISTEN_PORT);
    Socket tcpSocket = serverSocket.accept();
    InputStream sockIn = tcpSocket.getInputStream();
    int oneByte;
    while ((oneByte = sockIn.read()) > 0) { // '\0' will mean end of request.
      System.out.write(oneByte);
    }
    System.out.println();
    OutputStream sockOut = tcpSocket.getOutputStream();
    sockOut.write(MESSAGE.getBytes("US-ASCII"));
    sockOut.flush();
    tcpSocket.close();
    serverSocket.close();
  }

}

To run this code, start the server first, and then run the client from the same host.

Here are some notes pertaining to this code:



Threads and Work Queues

Programming with multiple threads can be tricky. The low-level constructs for threaded programming in Java are synchronized, Object.wait(), Object.notify(), and Object.notifyAll(). You have to have a very good understanding of how to use these constructs in order to write sophisticated multi-threaded programs.

I am giving an example of a common way to have threads communicate with each other, which is using queues. The following program uses two queues: inputQueue, which stores things that need to be computed upon, and resultQueue, which stores results of computations. The program has a total of two threads: the thread that calls main() and a daemon thread that is started by the program, which calls WorkQueue.run(). From now on I will call these threads the main thread and the worker thread, respectively.

The main thread passes work to the worker thread via the pushWork() method. It then receives results via popResult().

One very important note is that this program is written to assume that at most one thread ever calls pushWork(), and that at most one thread ever calls popResult() (these could potentially be two different threads, but in this program they are the same thread). If we needed to write a program that allowed multiple threads to call pushWork() and popResult(), we would have to re-engineer the parts of the program that deal with concurrency.

Here is the program:

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class WorkQueue implements Runnable {

  public static void main(String[] args) throws java.io.IOException {
    WorkQueue worker = new WorkQueue();
    Thread workerThread = new Thread(worker);
    workerThread.setDaemon(true); // #1
    workerThread.start();
    BufferedReader buffIn =
      new BufferedReader(new InputStreamReader(System.in));
    int[] inputPtr = new int[1];
    int[] resultPtr = new int[1];
    while (true) {
      System.out.println();
      System.out.println
        ("Input integer value for which to compute gamma, press Enter to see");
      System.out.print
        ("computed results, or type \"quit\" to terminate the program: ");
      String line = buffIn.readLine();
      if (line == null || line.equalsIgnoreCase("quit")) { break; }
      if (line.equals("")) {
        while (true) {
          if (!worker.popResult(inputPtr, resultPtr, -1)) {
            System.out.println("End of results"); break;
          }
          System.out.println
            (">>> Result for " + inputPtr[0] + " is " + resultPtr[0]);
        }
      }
      else {
        int input = 0;
        try { input = Integer.parseInt(line); }
        catch (NumberFormatException exc) {
          System.out.println("Could not parse input value as integer");
          continue;
        }
        if (!worker.pushWork(input)) {
          System.out.println("Work queue is full at the moment");
        }
        else {
          System.out.println("Your input value has been added to work queue");
        }
      }
    }
  }

  private static final int INPUT_QUEUE_LENGTH = 3;
  private int[] inputQueue = new int[INPUT_QUEUE_LENGTH];
  private int inputQueueHead = INPUT_QUEUE_LENGTH - 1;
  private int inputQueueTail = INPUT_QUEUE_LENGTH - 1;
  private int inputQueueSize = 0;

  private static final int RESULT_QUEUE_LENGTH = 8;
  private long[] resultQueue = new long[RESULT_QUEUE_LENGTH];
  private int resultQueueHead = RESULT_QUEUE_LENGTH - 1;
  private int resultQueueTail = RESULT_QUEUE_LENGTH - 1;
  private int resultQueueSize = 0;

  public WorkQueue() { }

  // This method to be called by at most one thread.
  public boolean pushWork(int input) {
    if (inputQueueSize == INPUT_QUEUE_LENGTH) { return false; } // #2
    inputQueue[inputQueueTail--] = input; // #3
    if (inputQueueTail < 0) { inputQueueTail = INPUT_QUEUE_LENGTH - 1; }
    synchronized (inputQueue) { // #4
      inputQueueSize++;
      inputQueue.notify();
    }
    return true;
  }

  // This method to be called by at most one thread.
  // If millisWait is 0, waits indefinitely; if negative, does not wait.
  public boolean popResult(int[] input, int[] result, long millisWait) {
    if (resultQueueSize == 0 && millisWait < 0) { return false; }
    long inputAndResult;
    synchronized (resultQueue) {
      if (resultQueueSize == 0) {
        try { resultQueue.wait(millisWait); }
        catch (InterruptedException exc) { }
        if (resultQueueSize == 0) { return false; }
      }
      inputAndResult = resultQueue[resultQueueHead];
      resultQueueSize--;
    }
    resultQueueHead--;
    if (resultQueueHead < 0) { resultQueueHead = RESULT_QUEUE_LENGTH - 1; }
    input[0] = (int) (inputAndResult >> 32);
    result[0] = (int) inputAndResult;
    return true;
  }

  public void run() {
    while (true) {
      int input;
      synchronized (inputQueue) { // #5
        while (inputQueueSize == 0) {
          try { inputQueue.wait(); }
          catch (InterruptedException exc) { }
        }
        input = inputQueue[inputQueueHead]; // #5a
        inputQueueSize--;
      }
      inputQueueHead--;
      if (inputQueueHead < 0) { inputQueueHead = INPUT_QUEUE_LENGTH - 1; }
      int result = computeGamma(input);
      if (resultQueueSize == RESULT_QUEUE_LENGTH) {
        System.err.println();
        System.err.println("Result queue full, discarding result " +
                           result + " for input " + input);
        continue;
      }
      resultQueue[resultQueueTail--] =
        (((long) input) << 32) | (0x00000000ffffffffl & result);
      if (resultQueueTail < 0) { resultQueueTail = RESULT_QUEUE_LENGTH - 1; }
      synchronized (resultQueue) {
        resultQueueSize++;
        resultQueue.notify();
      }
    }
  }

  public static int computeGamma(int input) {
    long inputLong = input;
    for (int i = 1; i <= 1000000000; i++) { inputLong = 31 * inputLong + i; }
    return (int) inputLong;
  }

}

The purpose of this program is to make some very CPU-intensive computations, which is encapsulated in the static method computeGamma(). However, we don't want the main thread to make these computations. The main thread has to deal with user interaction, and the program has to be very responsive to the user. We therefore have a worker thread perform the CPU-intensive computations while the main thread is free to deal with user interaction.

Here are some notes pertaining to this program: