Skip to content

Instantly share code, notes, and snippets.

@ArcticLight
Created October 25, 2014 02:37
Show Gist options
  • Save ArcticLight/019139fd1fe1e7d5552a to your computer and use it in GitHub Desktop.
Save ArcticLight/019139fd1fe1e7d5552a to your computer and use it in GitHub Desktop.
Thread safety in Java
import java.util.ArrayList;
public class Main {
/***************************************
* First, here's the complete code for an example that doesn't work.
* This is necessary so you can look at it and compare it with the
* patches we make so it *does* work.
***************************************/
/**
* This is really shitty code for a structure.
* As an example, we can see all kinds of racey behavior
* in the way I'm storing the strings.
* The basic intent is that we want to be able to put and pull
* strings from this structure, but as we'll see it's not going to work that way...
*/
public static class NotThreadSafePool {
ArrayList<String> strings;
public NotThreadSafePool() {
strings = new ArrayList<String>();
}
public void put(String s) {
strings.add(s);
}
public String pull() {
if(strings.size() >= 1) {
String r = strings.get(0);
strings.remove(0);
return r;
} else {
return "";
}
}
}
/**
* Here is a thread that we're going to run to repeatedly and as fast as we can grab
* things from the NotThreadSafePool.
*/
public static class YankThread implements Runnable {
NotThreadSafePool pool;
int num;
/**
* We need to maintain a reference to the pool so that we can yank from it.
* Also, store the thread number cuz that's cool.
*/
public YankThread(NotThreadSafePool pool, int num) {
this.pool = pool;
this.num = num;
}
@Override public void run() {
//only run this 30 times so the main method can display the demo properly
for(int i = 0; i < 30; i++) {
System.out.println("Yanked by " + num + ": " + pool.pull());
}
}
}
/**
* This is a spam thread: To demonstrate racy behavior, we're going to
* attempt to drop a consecutive sequence into the pool.
*/
public static class SpamThread implements Runnable {
NotThreadSafePool pool;
public SpamThread(NotThreadSafePool pool) {
this.pool = pool;
}
@Override public void run() {
//only run this 30 times so the main method can display the demo properly.
for(int i = 0; i < 30; i++) {
pool.put("" + i);
}
}
}
public static void main(String[] args) {
//First, let's run the unsafe demo.
//There are going to be 4 threads, two that pull, and two that push
//onto the data structure. They should, in theory, be two consecutive
//sets that interleave with each other, but we're not going to see that.
//If you get a good run, you should see any number
//of glitches due to race conditions; the numbers might not be consecutive
//when printed. You could get null. You could even see one number printed twice when it
//shouldn't have been. You could even see exceptions about ArrayIndexOutOfBounds from
//the ArrayList.
System.out.println("Beginning the unsafe demo:");
NotThreadSafePool pool = new NotThreadSafePool();
Thread spam1 = new Thread(new SpamThread(pool));
Thread yank1 = new Thread(new YankThread(pool,1));
Thread spam2 = new Thread(new SpamThread(pool));
Thread yank2 = new Thread(new YankThread(pool,2));
spam1.start();
spam2.start();
yank1.start();
yank2.start();
//try to wait for the demo to finish
try {
yank2.join();
} catch(InterruptedException t) {
//whatevs, it's not critical.
}
//Now let's run the synchronized demo. We should now have it be working!
//You still may see numbers out of order, but they shouldn't be seriously out of order.
//They should, if they're out of order, be due to thread-switching happening (and the
//order in which System.out.println is printing from the threads).
//Additionally, you should NOT see duplicates, nor should you see null or emptystring
//returned out of this demo. You will NOT get exceptions from this version of the demo.
System.out.println("Beginning the safe demo:");
pool = new ActuallyThreadSafePool();
spam1 = new Thread(new SpamThread(pool));
spam2 = new Thread(new SpamThread(pool));
yank1 = new Thread(new YankThread(pool,1));
yank2 = new Thread(new YankThread(pool,2));
spam1.start();
spam2.start();
yank1.start();
yank2.start();
}
public static class ActuallyThreadSafePool extends NotThreadSafePool {
// In effect, all this class is saying is that its methods are synchronized;
// we've basically just added the synchronized word to the method declaration.
@Override public synchronized String pull() {
return super.pull();
}
@Override public synchronized void put(String s) {
super.put(s);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment