<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Object Mentor Blog: Tag complete</title>
    <link>http://blog.objectmentor.com/articles/tag/complete</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description></description>
    <item>
      <title>So just what does synchronized do?</title>
      <description>&lt;p&gt;&lt;b&gt;Synopsis&lt;/b&gt;
&lt;Blockquote&gt;Using synchronized turns a huge state space into a comparatively small one.
&lt;/blockquote&gt;
&lt;p&gt;Normally, the light from a star radiates out in all directions. But what happens when a star collapses? There are several possibilities depending on the mass of the star. Our sun will turn into a red giant and then later turn into a white dwarf, giving out light from its accumulated heat for many years after living on Earth has become unbearable; mostly because of all the traffic.&lt;/p&gt;&lt;/p&gt;


&lt;p&gt;If the mass of the star is around 10x our star, its destiny is just a bit different. It will begin to collapse. Along the way, it will probably have some temporary reprieves from its ultimate fate, to become a Neutron star, by consuming other heavier elements such as carbon and helium . However, the writing is on the wall. The center of star, already an extreme environment, becomes even more so; eventually, the pressure from the collapsing of material into the center of the star results in a massive explosion known as a nova or super nova &amp;#8211; depending on the mass of the star. What is left is a neutron star. A neutron star is the heart of a pulsar, able to spin at amazing rates without pulling itself apart.&lt;/p&gt;

&lt;P&gt;Another option is a black hole. A black hole is an often used metaphor for either the effort expended on support/fixing/updating a big ball of mud or what the development effort appears to be in most of the time. Either way, it is not considered a good thing if you&amp;#8217;re anywhere in the vicinity.&lt;/P&gt;

&lt;p&gt;I&amp;#8217;m a pessimist by nature. I see a project in decline, sucking in other resources, like a black holes eating neighboring galaxies, snuffing the light out of stars, just like projects can suck the life force out of individuals.&lt;/P&gt;

&lt;P&gt;However, energy can escape from a black hole, eventually leading to the demise of the black hole. Energy can escape along the axis of rotation in either direction. In a sense, if we see the expansion of the black hole as a bad thing, anything that diminishes a black hole is a good thing. Stephen Hawking theorized that at the event horizon of black holes, fluctuations in the space can cause the spontaneous creations of pairs of particles and their anti particles. This is another way it can appear that a black hole is losing energy since it gives off particles while the opposite particle goes into the black hole.&lt;/P&gt;

	&lt;p&gt;Now I&amp;#8217;m off track. I wanted to talk about how synchronized give a similar effect as the massive gravity of black hole. But instead of curving space-time, they invert the possible state space of code in the presence of threads. Let&amp;#8217;s take a look.&lt;/p&gt;


Here&amp;#8217;s a simple class:
&lt;FONT style="line-height:95%"&gt;&lt;code&gt;&lt;pre&gt;
public class X {
   int value;

   public synchronized void incrementValue() {
        ++value;
    }
}&lt;/pre&gt;&lt;/code&gt;&lt;/FONT&gt;
Let&amp;#8217;s say, for the sake of argument, two threads are trying to execute the single method in this class. If the method is written like this:
&lt;FONT style="line-height:95%"&gt;&lt;code&gt;&lt;pre&gt;
   public synchronized void incrementValue() {
        ++value;
    }
&lt;/pre&gt;&lt;/code&gt;&lt;/FONT&gt;
&lt;P&gt;Then there are only two orderings, Thread 1/Thread 2, or Thread 2/Thread 1. All of those paragraphs at the top were to make this analogy.&lt;/p&gt;

&lt;P&gt;OK, so if we use synchronized that that&amp;#8217;s the black hole. The number of possible paths through the code for N threads is N!. What&amp;#8217;s the opposite? What happens if we take off the keyword and then run two threads through it? In how many ways can that single line of code get executed?&lt;/P&gt;

	&lt;p&gt;Are you ready for it?&lt;/p&gt;


&lt;B&gt;&lt;Blockquote&gt;3,432 different ways&lt;/blockquote&gt;&lt;/B&gt;

	&lt;p&gt;Really? Well at least conceptually. In practice, the Just In Time compiler will probably turn the &lt;span class="caps"&gt;JVM&lt;/span&gt; instructions into native code, so the actual number of individual steps will probably be less. But there really are that many paths through the system. You might ask how?&lt;/p&gt;


	&lt;p&gt;That single line of code is represented in byte code as 7 lines. I could show you the disassembly, but really, it&amp;#8217;s 7. You can convince yourself by having a look at the byte-code using a nice &lt;a href="http://download.forge.objectweb.org/eclipse-update/"&gt; byte-code viewer&lt;/a&gt; plug-in for eclipse.&lt;/p&gt;


	&lt;p&gt;What happens if we change the int to a long? We still have 7 lines of byte-code, but with different instructions. We have a total of 4 long reads/writes. Each long read/write requires two individual 32-bit operations &amp;#8211; or at lest the &lt;span class="caps"&gt;JVM&lt;/span&gt; can implement it that way. The actual number of atomic operations after the Just In Time compiler has had its way with your byte-code will probably be less, but if we stick to byte-codes and the Java memory model, the number 3,432 is now a whopping 705,432!&lt;/p&gt;


	&lt;p&gt;Let&amp;#8217;s extend that just a bit more. What if you have several lines of code? Each line of Java results in many more lines of byte-code. What if we have have 3 lies of Java? We probably have something like 21 lines of byte-code. How many paths of execution would 21 lines of byte-code executed by two threads have? 538,257,874,440!&lt;/p&gt;


	&lt;p&gt;Where the first program ended up being a white dwarf, the version using longs was a nova. I&amp;#8217;m not sure what to call the thee line Java method, maybe a hyper-nova?&lt;/p&gt;


	&lt;p&gt;In practice, the actual number of paths through a method will end up being smaller. And, most of the paths do not have any negative side-effects. The problem, however, is that there are a small number of hard to find paths of execution that do cause problems and finding them is like looking for a needle in a haystack.&lt;/p&gt;


	&lt;p&gt;Remember what adding the keyword synchronized did? If we use it on a three line method, it turns 538,257,874,440 into 2. It collapses the number of paths into 2 for two threads. And 2 is less than 538,257,874,440, for even vary large values of 2.&lt;/p&gt;


	&lt;p&gt;How did I derive at these magic numbers? Two ways.&lt;/p&gt;


	&lt;p&gt;I wanted to know how many possible orderings there were for 7 instructions and two threads. I knew it had to do with combinations and permutations but I just wasn&amp;#8217;t smart enough to figure it out. Searching on Google didn&amp;#8217;t do much for me either. So I decided to write a program to numerically derive the result. I tested my way into a program that would calculate the result.&lt;/p&gt;


The basic algorithm is:
&lt;ol&gt;
&lt;li&gt;Determine all possible orderings of each of the unique &amp;#8220;nodes&amp;#8221; in a system. For example, if I have 2 threads and 2 instructions, I can generate a natural key (ugh!) to represent each of the different combinations of threads and instructions: &lt;span class="caps"&gt;T1S1 T1S2 T2S1 2S2&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;Produce all valid permutations of these four &amp;#8220;nodes&amp;#8221;.&lt;/li&gt;
&lt;li&gt;Remove any that have invalid orderings.&lt;/li&gt;
&lt;li&gt;Count the results.&lt;/li&gt;
&lt;/ol&gt;

	&lt;p&gt;Many of my first implementations for some of these steps were exercises in writing really inefficient code! But having those tests made it really easy to swap out better algorithms when my brain caught up with what I was doing.&lt;/p&gt;


	&lt;p&gt;What is an invalid ordering? The code generated by the single line using the pre increment operator has 7 steps. Those 7 steps are in a single sequence with no looping and no conditionals. That&amp;#8217;s like saying A is always before B. So if we generate a list of orderings that end up including that combination, we&amp;#8217;ve generated an ordering that cannot happen.&lt;/p&gt;


So you can describe your possible state space in a few steps:
&lt;ol&gt;
&lt;li&gt;Register a node for each thread, step combination. 7 threads, 4 steps = 28 nodes.&lt;/li&gt;
&lt;li&gt;Register dependences between steps. For example, if I have seven steps and two threads executing the same sequence, I could describe this as: &lt;span class="caps"&gt;T1S1&lt;/span&gt; -&amp;gt; &lt;span class="caps"&gt;T1S2&lt;/span&gt; -&amp;gt; &lt;span class="caps"&gt;T1S3&lt;/span&gt;, etc. And for the second thread: &lt;span class="caps"&gt;T2S1&lt;/span&gt; -&amp;gt; &lt;span class="caps"&gt;T2S2&lt;/span&gt; -&amp;gt; T2&amp;gt;S3. Since the name is arbitrary, I just used letters, so in fact I had: a -&amp;gt; b -&amp;gt; c -&amp;gt; d -&amp;gt; e -&amp;gt; f -&amp;gt; g for one set of dependencies and h -&amp;gt; i -&amp;gt; j -&amp;gt; k -&amp;gt; l -&amp;gt; m -&amp;gt; o
&lt;/ol&gt;
&lt;p&gt;In fact, here&amp;#8217;s the test I used to describe that exact space:&lt;/P&gt;
&lt;FONT style="line-height:95%"&gt;&lt;code&gt;&lt;pre&gt;
    @Test
    public void twoThreadsAndSevenSteps() {
        calculator = new PathCalculator(14);
        createLine('a', 'b', 'c', 'd', 'e', 'f', 'g');
        createLine('h', 'i', 'j', 'k', 'l', 'm', 'n');
        start();
        int totalPaths = calculator.totalPaths();
        stop(2, 4, totalPaths);
    }
&lt;/pre&gt;&lt;/code&gt;&lt;/FONT&gt;

&lt;P&gt;By the time I got to this point, I wrote some &amp;#8220;tests&amp;#8221; using JUnit as a harness to run my code and display timing information and a summary of the &lt;results. The output looked like this.&lt;/p&gt;
&lt;FONT style="line-height:95%"&gt;&lt;code&gt;&lt;pre&gt;
Time:        4ms -- Threads:  3, Opcodes:  2, Total paths:      90
Time:        1ms -- Threads:  2, Opcodes:  3, Total paths:      20
Time:       54ms -- Threads:  4, Opcodes:  2, Total paths:    2520
Time:        3ms -- Threads:  2, Opcodes:  4, Total paths:      70
Time:       61ms -- Threads:  2, Opcodes:  5, Total paths:     252
Time:     1248ms -- Threads:  2, Opcodes:  6, Total paths:     924
Time:    12819ms -- Threads: 10, Opcodes:  1, Total paths: 3628800
&lt;/pre&gt;&lt;/code&gt;&lt;/FONT&gt;

I then used this information to fit a cure to my data and derive a formula for various combinations. For 2 threads, I came up with the following formula. If you let N be the number of steps:
&lt;blockquote&gt;Total Paths = 252 * 3.6667 &lt;sup&gt;N-5&lt;/sup&gt; &lt;/blockquote&gt;

&lt;P&gt;The morning after, Uncle Bob sent an email, showing me his work on how he calculated.&lt;/P&gt;

If you let:
&lt;ol&gt;
&lt;li&gt;T be the number of threads&lt;/li&gt;
&lt;li&gt;N be the number of steps&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;Total = (N*T)! &amp;nbsp; / &amp;nbsp; N! &lt;sup&gt;T&lt;/sup&gt;&lt;/blockquote&gt;

	&lt;p&gt;When I plugged numbers into his formula, they fit my formula. His formula generated an exact result as opposed to my estimate based on fitting a curve. Another thing his solution has going for it is the algorithm I came up with, I believe, is NP-complete. The net effect was I that while I was able to calculate result for 2 threads and 6 steps, 2 threads and 7 steps took a long time before running out of memory with a 2GB heap space. Bob&amp;#8217;s formula gave me a so-called verifier (as did my formula &amp;#8211; only Bob&amp;#8217;s was better) and the algorithm seems to grow based on factorials and not polynomials.&lt;/p&gt;


&lt;p&gt;What&amp;#8217;s the point of all of this? The single, one-line program, is a demonstration of why going from one thread to two threads makes writing/debugging/testing programs so much harder. It also numerically demonstrates &lt;b&gt;why&lt;/b&gt; the single responsibility principle is even more important when we start writing concurrent programs. The more you put in one place, the large the state space. Larger state spaces:
&lt;ol&gt;
&lt;li&gt;Make it harder to find that one or few number of paths that can cause your program to die, under load, in production, while you are on vacation&lt;/li&gt;
&lt;li&gt;Make your efforts to test (which is a sampling technique anyway) for concurrency issues all the more difficult&lt;/li&gt;
&lt;li&gt;Make it harder to find what might actually have caused a failure once you&amp;#8217;ve notice it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In a later post, I&amp;#8217;ll describe how we can improve our chances of covering this huge space of potential orderings.&lt;/P&gt;

&lt;p&gt;If you&amp;#8217;ve made it this far, wow! Congratulations!&lt;/P&gt;</description>
      <pubDate>Thu, 03 Jan 2008 21:27:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:5b8ba16d-124b-4957-a36a-eb8359b589c0</guid>
      <author>Brett Schuchert</author>
      <link>http://blog.objectmentor.com/articles/2008/01/03/so-just-what-does-synchronized-do</link>
      <category>Schuchert's Scattered Synapses </category>
      <category>threading</category>
      <category>concurrency</category>
      <category>testing</category>
      <category>NP</category>
      <category>complete</category>
    </item>
  </channel>
</rss>
