Created
October 25, 2014 02:37
-
-
Save ArcticLight/019139fd1fe1e7d5552a to your computer and use it in GitHub Desktop.
Thread safety in Java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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