Joseph Haugh
University of New Mexico
Process
Thread
Since saves are time-consuming for large files, Ul updates will stop each time the document is auto-saved.

What this looks like: a “laggy” user interface

class FileWorker extends Thread {
@Override
public void run() {
// save the file
}
}
// other methods, etc.
public static void main(String[] args) {
FileWorker worker = new FileWorker();
worker.start();
}
class FileWorker implements Runnable {
@Override
public void run() {
// save the file
}
}
// other methods, etc.
public static void main(String[] args) {
FileWorker worker = new FileWorker();
Thread workerThread = new Thread(worker);
workerThread.start();
}
public class Domino extends Thread {
private static int count = 0;
private Domino next;
private boolean standing = true;
public Domino(Domino next) {
// get/setName inherited from Thread
setName("" + count++);
this.next = next;
}
@Override
public void run() {
while(standing) {
// remain standing
}
if (next != null) { next.topple(); }
}
public void topple() {
standing = false;
System.out.println(Thread.currentThread().getName()
+ " toppled " + getName());
}
// Does this always work?
public static void main(String[] args) {
Domino d5 = new Domino(null);
Domino d4 = new Domino(d5);
Domino d3 = new Domino(d4);
Domino d2 = new Domino(d3);
Domino d1 = new Domino(d2);
d1.start();
d2.start();
d3.start();
d4.start();
d5.start();
d1.topple(); // topple the first domino
}
}
java.util.concurrent.atomicDoes the following code have a bug?
private static volatile int nextSerialNumber = 0;
public static int generateSerialNumber() {
return nextSerialNumber++;
}
private static final AtomicLong nextSerialNumber = new AtomicLong();
public static long generateSerialNumber() {
return nextSerialNumber.getAndIncrement();
}
Beginning with basic functionality, we will incrementally implement auto-save:
public class AutoSaveExecutor implements Runnable {
private int saveInterval;
private Document document;
public AutoSaveExecutor(int saveInterval, Document document) {
this.saveInterval = saveInterval;
this.document = document;
}
@Override
public void run() {
// spawn a new FileWorker every saveInterval...
}
}
public class Editor {
private Document curDoc = new Document();
private Thread autoSaveThread;
public Editor(boolean autoSaveEnabled) {
// Instantiation of Document state...
if(autoSaveEnabled) {
AutoSaveExecutor autoSaveExec =
new AutoSaveExecutor(60000, curDoc);
autoSaveThread = new Thread(autoSaveExec);
autoSaveThread.start();
}
}
// GUI rendering, associated methods...
}
public class Document {
private Path path;
public Path getPath() {
return path;
}
@Override
public String toString() {
// return a String representing the Document...
}
}
public class FileWorker implements Runnable {
private final Document docToSave;
public FileWorker(Document docToSave) {
this.docToSave = docToSave;
}
@Override
public void run() {
try {
BufferedWriter bufferedWriter =
Files.newBufferedWriter(docToSave.getPath());
bufferedWriter.write(docToSave.toString());
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class AutoSaveExecutor implements Runnable {
private int saveInterval;
public AutoSave(int saveInterval) {
this.saveInterval = saveInterval;
}
@Override
public void run() {
while(!Thread.interrupted()) {
try {
Thread.sleep(saveInterval);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Now that the AutoSaveExecutor wakes on an interval, we need it to dispatch FileWorker threads to perform the actual save operation
@Override
public void run() {
while (!Thread.interrupted()) {
try {
Thread.sleep(saveInterval);
FileWorker fileWorker = new FileWorker(document);
Thread fileWorkerThread = new Thread(fileWorker);
fileWorkerThread.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
The join operation waits for the active thread on which it’s called to die before proceeding
@Override
public void run() {
while(!Thread.interrupted()) {
try {
Thread.sleep(saveInterval);
FileWorker fileWorker = new FileWorker(document);
Thread fileWorkerThread = new Thread(fileWorker);
fileWorkerThread.start();
fileWorkerThread.join();
System.out.println ("Write completed at "
+ System.currentTimeMillis() );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
If our user wants to quit, we need to halt or interrupt the AutoSaveExecutor thread and join after the interrupt is processed
public void disableAutoSave() {
autoSaveThread.interrupt();
try {
autoSaveThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}