In this example we want to create a class that will generate and output to console, a random number between a range of two integers which are passed as arguments during the initialization.
public class SimpleRangeRandom implements Runnable {
private int min;
private int max;
private Thread thread;
public SimpleRangeRandom(int min, int max){
this.min = min;
this.max = max;
thread = new Thread(this);
thread.start();
}
@Override
private void WriteObject(ObjectOutputStreamout) throws IO Exception;
private void ReadObject(ObjectInputStream in) throws IOException, ClassNotFoundException;
public void run() {
while(true) {
Random rand = new Random();
System.out.println("Thread: " + thread.getId() + " Random:" + rand.nextInt(max - min));
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Now if we want to make this class Serializable there will be some problems. The Thread is one of the certain system-level classes that are not Serializable. So we need to declare the thread as transient. By doing this we will be able to serialize the objects of this class but we will still have an issue. As you can see in the constructor we set the min and the max values of our randomizer and after this we start the thread which is responsible for generating and printing the random value. Thus when restoring the persisted object by calling the readObject() the constructor will not run again as there is no creation of a new object. In that case we need to develop a Custom Serialization by providing two methods inside the class. Those methods are:
private void writeObject(ObjectOutputStream out) throws IOException;
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;
Thus by adding our implementation in the readObject() we can initiate and start our thread:
class RangeRandom implements Serializable, Runnable {
private int min;
private int max;
private transient Thread thread;
//transient should be any field that either cannot be serialized e.g Thread or any field you do not want serialized
public RangeRandom(int min, int max){
this.min = min;
this.max = max;
thread = new Thread(this);
thread.start();
}
@Override
public void run() {
while(true) {
Random rand = new Random();
System.out.println("Thread: " + thread.getId() + " Random:" + rand.nextInt(max - min));
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
thread = new Thread(this);
thread.start();
}
}
Here is the main for our example:
public class Main {
public static void main(String[] args) {
System.out.println("Hello");
RangeRandom rangeRandom = new RangeRandom(1,10);
FileOutputStream fos = null;
ObjectOutputStream out = null;
try
{
fos = new FileOutputStream("test");
out = new ObjectOutputStream(fos);
out.writeObject(rangeRandom);
out.close();
}
catch(IOException ex)
{
ex.printStackTrace();
}
RangeRandom rangeRandom2 = null;
FileInputStream fis = null;
ObjectInputStream in = null;
try
{
fis = new FileInputStream("test");
in = new ObjectInputStream(fis);
rangeRandom2 = (RangeRandom)in.readObject();
in.close();
}
catch(IOException ex)
{
ex.printStackTrace();
}
catch(ClassNotFoundException ex)
{
ex.printStackTrace();
}
}
}
If you run the main you will see that there are two threads running for each RangeRandom instance and that is because the Thread.start() method is now in both the constructor and the readObject().