So I don't actually remember the exact order I did things in any more, but this is more-or-less accurate. We started with some awesome code to run Conway's Game of Life. It had one major issue, though. I wanted to have a JProgressBar let the user know that information was being loaded over the internet. But it doesn't show up! There was that cool SwingUtilities.invokeLater, so we tried using that with a anonymous inner class, but now the thing didn't animate. The problem is that the thread that would do the animation is busy sleeping for 2500ms to simulate a slow internet connection, and that is the same thread that is responsible for drawing. So nothing appears to animate. The solution is to create a new Thread that will do the loading. By making this Thread start execution, the JVM creates a new thread in the background and then resumes running the method immediately. This allows the actionPerformed method to return control back to whoever called it (which is Swing), and Swing is then free to run drawing code. We had to use thread.start() and not thread.run() to do this, as thread.start() will actually spawn a new thread. I came up with a ludicrous idea. My computer has two cores on it. That means it can actually run two threads of execution at the same time as eachother. So, in theory, it would be possible to make the Game of Life run even faster by doing the board update in two threads. Of course, any speedup will be negligible. That is an important thing to remember about using threads, or any optimization. Don't just go ahead and "make something faster" because you think it needs to be. First, one should get the code working and then, if there are speed issues, worry about fixing them. A lot of times, you can actually make your code slower and harder to read by "optimizing" it. Anyways, We made a new update method that would update either the even rows or the odd rows of the board. We then made two threads, one for even and one for odd. First we tried: tEvn.start(); tOdd.start(); We noticed that squares were magically going away, disobeying the rules of the game. Cheaters. It turns out that when start() gets called, Java does the work it needs to launch the thread and get the method running, and then it returns back before the thread is "finished". So we were assigning in the new board data before the update had finished. Things in the upper-left maybe survived, but everything else got wiped. Then we went with: tEvn.run(); tOdd.run(); Of course, that isn't accomplishing anything because when you call run(), Java will use the same thread to call whatever the run() method of the thread does and return when that method is done. The solution is to use: tEvn.start(); tOdd.start(); tEvn.join(); tOdd.join(); The join method says to Java "don't move past the line until the thread is done" The order of the joins doesn't matter; most likely, both will finish at the same time anyway. There was a problem with had with the ArrayList crashing sometimes. Infrequently and seemingly random. This is a fun thing with threads; debugging is nigh impossible due to events never happening the same way twice. There are also a lot of problems with threaded programs running in debug mode (if you use debug mode). Some problems won't occur in debug mode, and some problems will only occur in debug mode. So code smart and think about what you are doing. The solution to the ArrayList was easy - it was getting the add method called twice at the same time and this was causing a crash. The solution was very easy; we just needed to wrap the ArrayList in a SynchronizedList using a static method in Collections. It was noted that the increment statements (++) didn't appear to be causing any havok. This is because Java supports atomic read and write on the primitive types other than doubles and floats. That means if one thread is reading or writing into that value, no other thread is allowed to touch it until that read or write has finished. Then I had an example that failed to work because I screwed up. We ended with a discussion (and solution) to a problem. I had a program that I wanted to be able to pause and not waste cpu cycles infinite looping when it was paused. I couldn't use Thread.sleep() because it required a time. And any other solution the class could think of involved an infinite loop to burn cpu cycles. That is a bad idea. So I mentioned condition variables. These are kind of communication corridors between threads. Each condition has an associated mutex (or lock, as Java calls them). I likened a mutex to the talking stick some people used in preschool. Only one thread can hold the lock at one time; if a thread requests the lock and it is currently in use, that thread will wait until the current owner releases the lock. Anyways, we used a condition variable to fix the problem, which involved a little bit of bug-ridden code that wasn't obviously bug-ridden. Our first attempt merely called condition.await() and then waiting to receive a signal for unpause. I mentioned that Java warns of "spurious wakeups" where sometimes a thread will be told to wake up even when the condition you are waiting on isn't true. So instead of writing: if () condition.await(); We should write: while () condition.await(); This insures that even if the thread receives a spurious wakeup, it will just go back to waiting if the condition isn't actually true. There was another subtle bug involving code that looks like: lock.lock(); /* some random * code that * does stuff */ lock.unlock(); What happens if the random code crashes? Does the lock get unlocked? Probably not. That means no one else can get the lock, so they just sit and wait and wait and do nothing. This is one version of deadlock (another occurs when thread A is waiting on thread B to finish, but thread B is waiting on thread A to finish). We fixed it by writing: try { lock.lock(); /* some random * code that * does stuff */ } finally { lock.unlock(); } A finally block is run no matter what happens in the try block, so we can insure the lock will in fact be released no matter what nefarious code does when it crashes (short of returning or stopping execution). I think I ended lecture by telling everyone who wanted more information to look on Wikipedia (which is apparently maintained by CSE majors). Their threading article is good, but the article on Semaphores (mutexes) has links to many of the classic kinds of problems in threading (chain smokers, producer-consumer, and many more exciting adventures).