?

Log in

~gnat/web/log
 
[Most Recent Entries] [Calendar View] [Friends]

Below are the 20 most recent journal entries recorded in Oleg Ignatenko's LiveJournal:

[ << Previous 20 ]
Sunday, February 19th, 2012
12:14 am
gone to Stack Overflow

image


Recently I decided to concentrate more on Stack Overflow / Stack Exchange Q&A sites instead of Live Journal.





profile for gnat on Stack Exchange, a network of free, community-driven Q&A sites



Monday, December 6th, 2010
9:47 pm
concurrencypedia

image


"It’s a kind of magic" (Queen)



"Concurrent Programming in Java" by Doug Lea (CPJ) is a wonderful book. Studying it turned out extremely helpful to me, connecting what looked before as isolated pieces of knowledge into solid and consistent system, providing a common vocabulary that makes it easier to understand concurrency concepts found in other sources.

image

This compact book covers surprisingly broad range of important topics, diving into sophisticated nuances when necessary.

Whether one tries to figure the usage of InterruptedException or limitations of embedded synchronization in Java - as well as drawbacks of alternative synchronization approaches, or why extending Thread might be not quite a good idea, or understand the difference between locks and monitors or get clear explanation regarding when and how to use notify vs notifyAll etc etc - it all can be found in CPJ. I thoroughly enjoyed reading great intro to Hoare's CSP theory in section 4.5.1 as well as insights on library (utility) oriented design specifics in 3.7 or flow networks in 4.2.

Section 4.4.1 contains an excellent presentation of fork/join decomposition, a functionality which is currently yet to be introduced in Java 7 - not quite bad for a book published more than 10 years ago huh?

Published more than 10 years ago... well have to admit, before reading this book I was somewhat concerned that it might turn out obsolete. My prior experience with concurrency sources dated prior to Java 5 was that these are a slippery matter. Thing is, Java 5 introduced quite fundamental changes in this area, making a lot of previous knowledge obsolete.

One example that comes to mind is Holub's Taming Java Threads. For concurrency matters and issues in earlier Java versions, this book presents much helpful information, but from post Java 5 perspective, it rather looks like a weird collection of misleading statements.

A different facet of issues related to post Java 5 syndrome can be seen in my favorite Jenkov's tutorial. This one was really helpful in my beginner's studies of multithreading and although it is based on earlier Java versions, most of what I've read there holds true today. Thing is, it covers only topics that remained unchanged in Java 5 (as if author deliberately tried to stick with stone-safe topics). The flipside of this approach is that many important modern concepts are not covered in this tutorial, making it's use quite limited.

As for CPJ, to me it looks like it passed acid test of Java 5 with flying colors. To my surprise, none of the topics felt missing, none felt obsolete. I honestly can't understand how author did that. Sure, given Lea's role in JSR-133 and JSR-166, his deep understanding of anticipated changes is natural but it still feels like magic to read up-to-date text written years before the actual date.

With all that said, I wouldn't state that CPJ sufficiently covers all the changes introduced in Java 5. To reflect these changes, author wrote an excellent "Quick guide on updates for J2SE5.0" available through Online supplement to CPJ at his home page.

For a more thorough study of modern Java concurrency, most appropriate further reading seems to be another great book, Java Concurrency in Practice by Brian Goetz (JCiP). I think it is worth noting that books by Lea and Goetz not only complement each other but there seems to be certain err synergy between them. Interesting that both authors recommend each other's book in quite strong terms.

In concurrency interest mailing list Lea explains why he didn't make a new edition after Java 5: "There were plans for it, but Java Concurrency in Practice (http://jcip.net/) included all of the updates to cover Java5 that CPJ3e would have included."

In turn, Goetz in his book says, "Java Concurrency in Practice is not an ... encyclopedic reference for All Things Concurrency - for that, see Concurrent Programming in Java"

Style wise book by Goetz was a smoother, easier reading to me. CPJ in the beginning felt rather dry in comparison. Howere I soon learned to appreciate it's laconic presentation style - another thing that I just can't understand how author did it. That unique balance of clarity, brevity and completeness sometimes felt like pure magic.

Saturday, October 23rd, 2010
10:08 am
I want to be replaceable

image
You're original with your own path You're original, got your own way (Leftfield)

I want to be replaceable...

  • ...so that other guy checking my stuff from repository doesn't have to struggle with unmaintainable code
  • ...so that other guy looking at my records in issue tracker doesn't need my help to figure what I was thinking about while working on it
  • ...so that other guy reading my wiki pages doesn't need me to explain stuff documented there
  • ...so that I can enjoy looking how stuff I made grows and flourishes, living its own life
    image

...so that I can focus on doing new, original stuff without being distracted with worries about what left behind.

Thursday, October 14th, 2010
9:45 am
"Getting Things Done" by David Allen

image
"The Art of Stress-Free Productivity" (book subtitle)

Great manual for PDA. :) If seriously, I learned quite a few useful features of my PDA while studying this book.

image

For those who haven't read it, there is a brief high level overview in Wikipedia.

Allen's method should actually sound familiar to most software engineers and managers because it pretty much resembles issue tracking systems work flow: write down, list, categorize, review. Given the proven usefulness of issue tracking in software projects, it doesn't sound surprising that similar approach succeeds in other areas.

What was new to me and what was different from the way I get used to work with issue trackers is handling of action priorities. It was particularly interesting to find a compelling explanation of how ignoring "low priority" actions can negatively impact overall productivity (in chapter 11). Upon further thinking of it, I would say that this new concept indeed matches my observations on software issue tracking.

  • Really, just think for example of code quality issues. Traditionally, these tend to get low priority - many project managers fall into that trap. "Customers don't complain about code quality which means it's unimportant."

    This logic sounds reasonable while you don't pay attention to programmer's productivity - here, things might really be going just as Allen describes it. Wonder why work on high priority issues takes more time than you expected? maybe it's time to also wonder whether code quality is sufficient to let programmers work productively.

Overall, the book presents reasonable, proven ("software issue tracking") and easy to follow method of time management. Some particular tips were true revelations to me. Say, if you feel too tired to do some Big Deal, try switching to something smaller and easier. This simple trick turned out surprisingly useful. Interesting that it also has certain "charging" effect. I mean after "little thing" done you might find out that you've got more energy and can return and complete that Big Deal.

  • One thing I haven't yet fully groked is weekly reviews. Usually, these are fun and cool - stress-free productivity indeed. But sometimes these turn into "bursts of stress", taking quite much time and effort and ending in frustration instead of usual relief. I haven't yet figured why it happens and what to do about it.

    Maybe this is like it was to me before with weekly reports - in some software projects I participated it was required or recommended to write such reports (useful "feature" by the way). Writing weekly reports also took some time to get used to, and at first it also sometimes felt frustrating.

Useful book, interesting method.

Saturday, October 2nd, 2010
5:24 pm
funny "fact" in LinkedIn profile

image

"truth, the whole truth..." (Sworn testimony)

Once I had an interesting conversation with a friend. He came to me to ask about my ex-colleague Superman (name is fictional).

- Hey Oleg, I recently came across Superman's profile in LinkedIn and since you've been working in the project he refers to, would like to check if what he writes is correct:

"personally designed and coded Component component for project Project"

- Well... hmmm... this fact he writes about is true.

- Wow, now that's a surprise! But... so far I have known him as an inferior programmer. How come that he created this component? I know it's rather sophisticated and works well. Hmm strange - do I miss something here?

- Well, you actually do. You just miss the fact not mentioned in his profile. He created the initial version, but later we found out that it's FUBAR. Because of that, we had to throw away all his code and rewrite this component from the scratch. I can show you the records in project bug database about that incident. So, the component you actually use has nothing in common with initial Superman's design and code.

- ROTFL!!! Now that makes much more sense to me - thanks!

Later, we also looked at the bug database records I mentioned and had another portion of good laughs. There were long lists of various WTFs written by component reviewers and all the records had the same summary written by the guy assigned to rewrite that broken component:

"Code was deleted from repository due to its low quality, lack of documentation and absense of support from the author. All the functionality was moved to new code."

Thursday, September 23rd, 2010
11:30 pm
Expert One-on-One J2EE Development without EJB

image

Joy, beautiful spark of divinity
Daughter of Elysium,
We enter, drunk with fire,
Into your sanctuary...
(Ode to Joy)



image

"I wrote this book for architects and developers who have grown increasingly frustrated with traditional approaches to J2EE design, especially EJB. It shows what you can do right now to implement cleaner, more productive alternatives to EJB and move into the next era of web applications." (Rod Johnson)




How to monetize an object-oriented design. :)

Author has a distinct talent to clearly explain complicated matters. Coupled with his apparently enormous experience, it makes a killer mix that forced me to literally swallow the book - page by page, chapter by chapter.

First five chapters were fascinating reading.

Chapters 6 through 15 cover a broad range of enterprise software technologies, practices and frameworks. AOP and DAO sections were particularly helpful. Web Tier chapter turned out rather difficult to read; it felt more heavyweight compared to the rest of the book. Testing and Testability chapter is excellent, it certainly shed some light on why author was as successful in leading Spring development.

Two closing chapters (Sample App and Conclusion) make a powerful coda to this middleware symphony, reinforcing opening theme of superiority of clean object oriented design.

Reading 'Last Words' brought back memories of listening to Beethoven's Ode to Joy (btw Johnson's bio mentions that he's got a degree in Musicology)




"We believe not only that J2EE development should be much simpler than the mixture of drudgery and complexity it's often made out to be, but that developing J2EE applications should be fun.

We hope you've enjoyed reading this book as much as we enjoyed writing it. We hope that after reading it you'll be familiar with the architectural alternatives to the traditional approach to J2EE architecture. We encourage you to base your architectural choices on evidence. While we hope we've been persuasive, don't take our word without proof. Before committing to an application architecture, verify that whatever approach you choose delivers on the goals for your project. And have fun!"



image


Amazing book, truly amazing.





Wednesday, September 1st, 2010
11:55 pm
troubleshooting "401 error" in Maven release:perform

image
"who the hell are you?" (Predator)


Recently got 401 error while trying maven release:perform. That happened in a new project and I already had problems like that at this machine so I was expecting some complicated investigation to get it fixed.

This time though, troubleshooting turned out surprisingly simple - mostly due to the fact that colleague already had some other project successfully released to the same repository from his machine.

  • So, first thing we tried was to release "my" project from colleague's machine - to find out if it will work at proven machine with proven credentials.
  • We were lucky - trial release of the project from colleague's machine turned out successful. Next thing we tried was to release "my" project at my machine with colleague's Maven credentials (username and password) - to learn if it will work on my machine with credentials that were proven to work.
  • Woo-hoo it worked too. With this data, it turned out easy to approach the guy who administered Maven repository asking him to simply check what's wrong / different in my credentials compared to those of my colleague. I didn't dig into further details, one thing I know is that my password was reset and that did the trick.

So simple, really simple... I just love when things work that way.

The only cumbersome part of it was that because of my project being fairly large every trial took us about an hour to do - while it was building, uploading etc etc. Also, there was rather ugly side effect of three "fake" project releases resulting from successful trials: 1 - successful test on colleague's machine, 2 - successful test on my machine with colleague's credentials and finally, 3 - successful test with my fixed credentials. Of course, none of these releases had anything to do with development in the project.

  • Messing with these fake releases reminded me that in one of my past teams, we used to have a special project which only purpose was to smoke test basic issues with Maven - proper settings, authorisation and other stuff not related to programming. That special project was called something like fake.jar, it had minimal pom, bare minimum of sources and dependencies etc. As a result, building and deploying it was lightning fast and doind various experiments to troubleshoot Maven was really easy. And, of course, successful trial releases done with that special project did not mess into real development projects.

image

Saturday, August 21st, 2010
12:52 pm
tips to maintain team wiki

image
"With a Little Help from My Friends" (Beatles)


Been recently working a lot with project Confluence pages. Though their contents and looks varied a lot, I noticed some repeating patterns, some set of rules I tried to follow. Decided to write it down here.






fundamentals



 value feedback

  • ask for, collect and record any feedback you can get - mail, confluence page comments (most convenient btw), messenger, conversation

    • recording feedback

      • Q: what if I currently don't have time to properly process the information?

      • A: save it at the page as-is - for future processing and hide it from regular readers like as follows: {excerpt:hidden=true}information to be processed later{excerpt}



    • processing feedback

      • try to do it as fast as possible. Details and context that look obvious to you or submitter right now, can be forgotten a day or week or month later

      • consider alternative solutions to problem (s) pointed in feedback. Example:
        - (feedback) hey the information I need is mixed with useless one
        - (wrong action) OK I remove stuff you don't need
        - (right action) OK I will split the information
          to one of your interest and the rest that
          might be of interest for someone else






 handling long term work

  • handling suggestions implying long term work

    • Q: what if suggestion is like say "collect and rearrange all relevant data documented at thousand other pages"?

    • A: add a TODO-s section to your page (if it's not there yet) and record the suggestion in this section. Later, you can use it also to track your progress
              update DD.MM.YYYY: 475 pages of 1000 are processed




 suggestions that look wrong to you

  • handling suggestions that look wrong to you
    side note it never hurts to ask the suggestion submitter for clarification. Though, you'd better do your part of job first:

    • check the page history, taking into account review date. There is always a chance that submitter points to the issue from older version that is already corrected

    • look closer at the page/section referred to and ask yourself, what could cause him/her to think that way?

    • (after a little training with trick like above) you'll find out that in most if not all cases, there is a distinct area for improvement there.

      • Imagine for example, user complaining about missing information which is actually present somewhere in the page. Despite looking wrong on the surface, such a complaint often indicates a serious problem at the page: namely that some important information is hard to find. Giving it a visibility it deserves will improve the page.






 better fast than perfect


  • rely on review and feedback. If something really needs correction, time will sort it out for you

    • this also applies to self-review. Record the draft of what you want to put to page and review it yourself an hour (or day, or week) later - this trick can do wonders

    • don't waste time on making things perfect at the first try - it's impossible

      • record the information as soon as it looks barely acceptable and ask those interested to review it






 be bold





advanced



 be thankful

  • People giving feedback are a valuable asset, be thankful to them. These people took their time and effort to not only read your page, but also to provide their feedback. Vast majority of your users won't be that generous to you. Those sharing their thoughts are "the cream" of your audience, be thankful for their contribution.


 explain TLA-s

  • if you can't figure what's TLA? - you've got the point

    • use links for that if possible: TLA




 record answers to questions

  • respect others time - record answers

    • It might take you one second to answer to question, but think about the guy who asked it instead? (S)he took time to read your page, to try to find the answer, to contact you and wait for your answer. If you record the answer at the page, you'll save all that time for next guy having this question.

    • record answers to your own questions. What is unclear to you, might be unclear to the next reader.




 use question marks

  • when in doubt, use question marks

    • Confluence syntax:(?)
      Example: image<this info> needs checking image

    • this way, any reader can clearly see what sort of info you need; chances are high that someone can help you clarify things




 links are your friends

  • DRY - don't try to copy information when you can provide the link instead

  • learn to summarize when needed

    • some link
      - hey what's this?

    • some link - Wikipedia, the free encyclopedia that anyone can edit.




 visuals matter


  • when you have time, self-review your page asking yourself if it is easy for the reader to get the point(s) you are willing to make

  • it can be quite difficult to find substance in an unstructured stream of consciousness
    ...I gave him all the pleasure I could leading him on till he asked me to say yes and I wouldnt answer first only looked out over the sea and the sky I was thinking of so many things he didnt know of Mulvey and Mr Stanhope and Hester and father and old captain Groves and the sailors playing all birds fly and I say stoop and washing up dishes they called it on the pier and the sentry in front of the governors house with the thing round his white helmet poor devil half roasted and the Spanish girls laughing in their shawls and their tall combs and the auctions in the morning the Greeks and the jews and the Arabs and the devil knows who else from all the ends of Europe and Duke street and the fowl market all clucking outside Larby Sharons and the poor donkeys slipping half asleep and the vague fellows in the cloaks asleep in the shade on the steps and the big wheels of the carts of the bulls and the old castle thousands of years old yes and those handsome Moors all in white and turbans like kings asking you to sit down in their little bit of a shop and Ronda with the old windows of the posadas 2 glancing eyes a lattice hid for her lover to kiss the iron and the wineshops half open at night and the castanets and the night we missed the boat at Algeciras the watchman going about serene with his lamp and O that awful deepdown torrent O and the sea the sea crimson sometimes like fire and the glorious sunsets and the figtrees in the Alameda gardens yes and all the queer little streets and the pink and blue and yellow houses and the rosegardens and the jessamine and geraniums and cactuses and Gibraltar as a girl where I was a Flower of the mountain yes when I put the rose in my hair like the Andalusian girls used or shall I wear a red yes and how he kissed me under the Moorish wall and I thought well as well him as another and then I asked him with my eyes to ask again yes and then he asked me would I yes to say yes my mountain flower and first I put my arms around him yes and drew him down to me so he could feel my breasts all perfume yes and his heart was going like mad and yes I said yes I will Yes.


 relax and have fun


  • keep in mind that typically readers of your page are not required to be dead serious

    image

    • when you feel it's appropriate to have fun - have it




 fight link rot

  • You are willing to move some page or resource? Fine, just think about the guys keeping links to it somewhere in their bookmarks, emails archives, documents etc etc

  • when you (re)move the page or a document, keep a placeholder where it previously was located, to help visitors understand what happened to it and where to go instead

    • <this page> has been moved to <that page>

    • <this document> has been removed because of <the reason>










References for further reading










image





Friday, August 6th, 2010
11:32 pm
Java 7 concurrency utilities: Face the Phaser, phase 4

image



        




Upon a closer look at Phaser API, it turns out rather convenient to arrange synchronised work when there is an arbitrary number of tasks involved, eg developers, testers, bug fixers or when there is an arbitrary number of phases (work until there are no bugs found). Let's see how to do that kind of stuff:



import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Phaser;
import java.util.concurrent.atomic.AtomicInteger;

class FaceThePhaser4 {
    private static final int PHASES_PER_SPRINT = 2;
    private final BugSource bugSource = new BugSource();

    public static void main(String[] args) {
        new FaceThePhaser4().manageDevelopment();
    }

    public void manageDevelopment() {
        runDevelopment(
                Arrays.asList(new Developer(), new Developer()),
                Arrays.asList(new Tester(), new Tester()),
                2);
    }

    private void runDevelopment(final Iterable<Developer> devTeam,
            final Iterable<Tester> qaTeam, final int sprintsPlanned) {
        final int sprints = sprintsPlanned * PHASES_PER_SPRINT;
        log("manager: planned sprints: " + sprintsPlanned);
        final Phaser scrumMaster = new Phaser() {
            @Override
            protected boolean onAdvance(int phase, int registeredParties) {
                log("scrum master: sprint " + (phase / PHASES_PER_SPRINT + 1)
                        + " - phase " + (phase % PHASES_PER_SPRINT + 1)
                        + " completed");
                if (registeredParties == 0) {
                    log("ERROR: no participants");
                    return true;
                }
                return false;
            }
        };
        scrumMaster.register();
        Iterable<BugFixer> fixTeam = new ArrayList<BugFixer>();
        while (!scrumMaster.isTerminated()) {
            final String sprint = describeSprint(scrumMaster);
            log("manager: coding, " + sprint);
            runCycles(scrumMaster, fixTeam);
            runCycles(scrumMaster, devTeam);
            scrumMaster.arriveAndAwaitAdvance();
            log("manager: testing, " + sprint);
            runCycles(scrumMaster, qaTeam);
            scrumMaster.arriveAndAwaitAdvance();
            bugSource.newBugs();
            log("manager: checking results of " + sprint);
            fixTeam = handleBugs();
            if (okToRelease(scrumMaster, sprints)) {
                scrumMaster.forceTermination();
            }
        }
        log("manager: bye-bye");
    }

    private boolean okToRelease(Phaser scrumMaster, int sprints) {
        if (scrumMaster.getPhase() < sprints - 1
                || bugSource.numOfBugs() != 0) {
            return false;
        }
        log("manager: ok to release");
        return true;
    }

    private void runCycles(final Phaser scrumMaster,
            Iterable<? extends Manageable> team) {
        for (Manageable manageable : team) {
            manageable.runSprint(scrumMaster);
        }
    }

    private Iterable<BugFixer> handleBugs() {
        final int numBugs = bugSource.numOfBugs();
        log("bugs found: " + numBugs);
        final List<BugFixer> fixTeam = new ArrayList<BugFixer>();
        if (numBugs == 0) {
            return fixTeam;
        }
        log("need additional resources to fix bugs");
        for (int i = 0; i < numBugs; i++) {
            fixTeam.add(new BugFixer());
        }
        return fixTeam;
    }
    
    private static String describeSprint(Phaser scrumMaster) {
        final int phase = scrumMaster.getPhase();
        return phase < 0 ? "last sprint" : "sprint "
                + (phase / PHASES_PER_SPRINT + 1);
    }

    private static void log(String msg) { System.out.println(msg); }

    private static class BugSource {
        private static final int FORCE_ZERO_BUGS = 3;
        private static final Random bugLuck = new Random();
        private final Object lock = new Object();
        private int callCounter = 0;
        private int numOfBugs = 0;

        public void newBugs() {
            synchronized (lock) {
                if (++callCounter > FORCE_ZERO_BUGS) {
                    numOfBugs = 0;
                    return;
                }
                numOfBugs = bugLuck.nextInt(2) + 1;
            }
        }
        public int numOfBugs() {
            synchronized (lock) {
                return numOfBugs;
            }
        }
    } // BugSource

    private static class BugFixer extends Developer {
        @Override public void work() { log(toString() + ": bug fixing"); }
    } // BugFixer

    private static class Developer extends Engineer {
        private static final AtomicInteger idSource = new AtomicInteger();
        private final int id = idSource.incrementAndGet();
        @Override public void work() { log(toString() + ": coding"); }
        @Override public String toString() { return "developer #" + id; }
    } // Developer

    private static class Tester extends Engineer {
        private static final AtomicInteger idSource = new AtomicInteger();
        private final int id = idSource.incrementAndGet();
        public void work() { log(toString() + ": testing"); }
        @Override public String toString() { return "tester #" + id; }
    } // Tester

    private abstract static class Engineer implements Manageable {
        public void runSprint(final Phaser scrumMaster) {
            scrumMaster.register();
            new Thread() {
                @Override
                public void run() {
                    final String sprint = describeSprint(scrumMaster);
                    log("scrum: " + sprint + " for " + Engineer.this);
                    work();
                    log("scrum: " + sprint + " - work done for "
                            + Engineer.this);
                    scrumMaster.arriveAndDeregister();
                }
            }.start();
        }
    } // Engineer

    private static interface Manageable {
        void runSprint(Phaser scrumMaster);
        void work();
    } // Manageable
} // FaceThePhaser4

        
example output:
manager: planned sprints: 2
manager: coding, sprint 1
scrum: sprint 1 for developer #1
developer #1: coding
scrum: sprint 1 - work done for developer #1
scrum: sprint 1 for developer #2
developer #2: coding
scrum: sprint 1 - work done for developer #2
scrum master: sprint 1 - phase 1 completed
manager: testing, sprint 1
scrum: sprint 1 for tester #2
tester #2: testing
scrum: sprint 1 - work done for tester #2
scrum: sprint 1 for tester #1
tester #1: testing
scrum: sprint 1 - work done for tester #1
scrum master: sprint 1 - phase 2 completed
manager: checking results of sprint 1
bugs found: 2
need additional resources to fix bugs
manager: coding, sprint 2
scrum: sprint 2 for developer #3
developer #3: bug fixing
scrum: sprint 2 - work done for developer #3
scrum: sprint 2 for developer #1
developer #1: coding
scrum: sprint 2 for developer #4
scrum: sprint 2 - work done for developer #1
scrum: sprint 2 for developer #2
developer #2: coding
scrum: sprint 2 - work done for developer #2
developer #4: bug fixing
scrum: sprint 2 - work done for developer #4
scrum master: sprint 2 - phase 1 completed
manager: testing, sprint 2
scrum: sprint 2 for tester #1
tester #1: testing
scrum: sprint 2 - work done for tester #1
scrum: sprint 2 for tester #2
tester #2: testing
scrum: sprint 2 - work done for tester #2
scrum master: sprint 2 - phase 2 completed
manager: checking results of sprint 2
bugs found: 2
need additional resources to fix bugs
manager: coding, sprint 3
scrum: sprint 3 for developer #5
developer #5: bug fixing
scrum: sprint 3 - work done for developer #5
scrum: sprint 3 for developer #1
scrum: sprint 3 for developer #2
scrum: sprint 3 for developer #6
developer #1: coding
developer #2: coding
scrum: sprint 3 - work done for developer #2
developer #6: bug fixing
scrum: sprint 3 - work done for developer #1
scrum: sprint 3 - work done for developer #6
scrum master: sprint 3 - phase 1 completed
manager: testing, sprint 3
scrum: sprint 3 for tester #1
tester #1: testing
scrum: sprint 3 - work done for tester #1
scrum: sprint 3 for tester #2
tester #2: testing
scrum: sprint 3 - work done for tester #2
scrum master: sprint 3 - phase 2 completed
manager: checking results of sprint 3
bugs found: 1
need additional resources to fix bugs
manager: coding, sprint 4
scrum: sprint 4 for developer #1
developer #1: coding
scrum: sprint 4 - work done for developer #1
scrum: sprint 4 for developer #2
developer #2: coding
scrum: sprint 4 - work done for developer #2
scrum: sprint 4 for developer #7
developer #7: bug fixing
scrum: sprint 4 - work done for developer #7
scrum master: sprint 4 - phase 1 completed
manager: testing, sprint 4
scrum: sprint 4 for tester #2
scrum: sprint 4 for tester #1
tester #2: testing
tester #1: testing
scrum: sprint 4 - work done for tester #2
scrum: sprint 4 - work done for tester #1
scrum master: sprint 4 - phase 2 completed
manager: checking results of sprint 4
bugs found: 0
manager: ok to release
manager: bye-bye


Last updated: July 16, 2012


Monday, August 2nd, 2010
11:42 pm
Java 7 concurrency utilities: Face the Phaser, phase 3

image



        




This code example is quite a minor variation of a previous one. Not that it looks especially interesting or innovative, but since it is mentioned in API documentation, let's give it a shot anyway. "If the main task must later await termination..."


Well, code in this example is pretty much the same as in the previous one, with only some additional Phaser activity in the main thread. Here we can see same "guys" doing same stuff. "Developers are doing coding" in their threads and Phaser "scrum master" controls iterations just the same way as it was done in previous example. The only difference is that main thread doesn't quit after starting all "developer" threads but instead, continues to run under control of the Phaser. It is as if there is a "manager", a guy who checks results of each Scrum sprint iteration.


Main thread ("manager") uses Phaser's method arriveAndAwaitAdvance() to wait until "developer" threads are completed and to perform "checking results of sprint" after that. This causes thread to wait until Phaser notifies it (until "all developers completed the sprint"). Pretty similar to how "developer" threads do that, "manager" also invokes Phaser's method isTerminated() to check if it's time to stop iterating.




import java.util.Arrays;
import java.util.concurrent.Phaser;
import java.util.concurrent.atomic.AtomicInteger;

class FaceThePhaser3 {
    public static void main(String[] args) {
        new FaceThePhaser3().manageDevelopment();
    }

    public void manageDevelopment() {
        runDevelopment(Arrays.asList(
                new Developer(),
                new Developer()), 2);
    }

    // API doc Java 7 DRAFT ea-b102
    //  "If the main task must later await termination, it may re-register
    //   and then execute a similar loop"
    private void runDevelopment(Iterable<Developer> team, final int sprints) {
        log("manager: sprints: " + sprints);
        final Phaser scrumMaster = new Phaser() {
            @Override
            protected boolean onAdvance(int phase, int registeredParties) {
                log("scrum master: sprint " + (phase + 1)
                        + " - all developers completed");
                return phase >= sprints - 1 || registeredParties == 0;
            }
        };
        scrumMaster.register();
        for (final Developer developer : team) {
            scrumMaster.register();
            log("manager: welcome " + developer);
            new Thread() {
                @Override public void run() {
                    log("scrum: run tasks for " + developer);
                    do {
                        final String sprint = describeSprint(scrumMaster);
                        log("scrum: " + sprint + " for " + developer);
                        developer.run();
                        log("scrum: " + developer
                                + ", please wait for completion of " + sprint);
                        scrumMaster.arriveAndAwaitAdvance();
                    } while (!scrumMaster.isTerminated());
                    log("scrum: bye-bye " + developer);
                }
            }.start();
        }
        log("manager: welcoming complete");
        while (!scrumMaster.isTerminated()) {
            final String sprint = describeSprint(scrumMaster);
            log("manager: waiting for completion of " + sprint);
            scrumMaster.arriveAndAwaitAdvance();
            log("manager: checking results of " + sprint);
        }
        log("manager: bye-bye");
    }
    
    private String describeSprint(Phaser scrumMaster) {
        final int phase = scrumMaster.getPhase();
        return phase < 0 ? "last sprint" : "sprint " + (phase + 1);
    }

    private static void log(String msg) { System.out.println(msg); }

    private static class Developer implements Runnable {
        private final static AtomicInteger idSource = new AtomicInteger();
        private final int id = idSource.incrementAndGet();
        public void run() { log(toString() + ": coding"); }
        @Override public String toString() { return "developer #" + id; }
    } // Developer
} // FaceThePhaser3


        
example output:
manager: sprints: 2
manager: welcome developer #1
manager: welcome developer #2
manager: welcoming complete
manager: waiting for completion of sprint 1
scrum: run tasks for developer #1
scrum: sprint 1 for developer #1
developer #1: coding
scrum: developer #1,
 please wait for completion of sprint 1
scrum: run tasks for developer #2
scrum: sprint 1 for developer #2
developer #2: coding
scrum: developer #2,
 please wait for completion of sprint 1
scrum master: sprint 1 - all developers completed
scrum: sprint 2 for developer #2
developer #2: coding
scrum: developer #2,
 please wait for completion of sprint 2
scrum: sprint 2 for developer #1
developer #1: coding
scrum: developer #1,
 please wait for completion of sprint 2
manager: checking results of sprint 1
manager: waiting for completion of sprint 2
scrum master: sprint 2 - all developers completed
manager: checking results of sprint 2
manager: bye-bye
scrum: bye-bye developer #2
scrum: bye-bye developer #1


Just like in previous example, here is code to do about the same in Java 5 and 6 with CyclicBarrier:




import java.util.Arrays;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicInteger;

class NoBigDeal3 {
    private volatile boolean sprintsCompleted = false;
    public static void main(String[] args) {
        new NoBigDeal3().manageDevelopment();
    }

    public void manageDevelopment() {
        runDevelopment(Arrays.asList(
                new Developer(),
                new Developer()), 2);
    }

    private void runDevelopment(List<Developer> team, final int sprints) {
        log("manager: sprints: " + sprints);
        final CyclicBarrier scrumMaster = new CyclicBarrier(team.size() + 1,
                new Runnable() {
                    private int phase = 0;
                    public void run() {
                        if ( ++phase >= sprints) {
                            sprintsCompleted = true;
                        }
                        log("scrum master: sprint " + phase
                                + " - all developers completed");
                    }
                });
        for (final Developer developer : team) {
            log("scrum master: welcome " + developer);
            new Thread() {
                private int phase = 0;
                @Override public void run() {
                    log("scrum: run tasks for " + developer);
                    do {
                        final String sprint = describeSprint(++phase);
                        log("scrum: " + sprint + " for " + developer);
                        developer.run();
                        log("scrum: " + developer
                                + ", please wait for completion of " + sprint);
                        await(scrumMaster);
                    } while (!sprintsCompleted);
                    log("scrum master: bye-bye " + developer);
                }
            }.start();
        }
        log("manager: welcoming complete");
        int phase = 0;
        while (!sprintsCompleted) {
            final String sprint = describeSprint(++phase);
            log("manager: waiting for completion of " + sprint);
            await(scrumMaster);
            log("manager: checking results of " + sprint);
        }
        log("manager: bye-bye");
    }

    private void await(CyclicBarrier cb) {
        try {
            cb.await();
        } catch (InterruptedException ie) {
            throw new RuntimeException(ie);
        } catch (BrokenBarrierException bbe) {
            throw new RuntimeException(bbe);
        }
    }

    private String describeSprint(int phase) {
        return sprintsCompleted ? "last sprint" : "sprint " + phase;
    }

    private static void log(String msg) { System.out.println(msg); }

    private static class Developer implements Runnable {
        private final static AtomicInteger idSource = new AtomicInteger();
        private final int id = idSource.incrementAndGet();
        public void run() { log(toString() + ": coding"); }
        @Override public String toString() { return "developer #" + id; }
    } // Developer
} // NoBigDeal3


        
example output:
manager: sprints: 2
scrum master: welcome developer #1
scrum master: welcome developer #2
manager: welcoming complete
manager: waiting for completion of sprint 1
scrum: run tasks for developer #2
scrum: sprint 1 for developer #2
developer #2: coding
scrum: developer #2,
 please wait for completion of sprint 1
scrum: run tasks for developer #1
scrum: sprint 1 for developer #1
developer #1: coding
scrum: developer #1,
 please wait for completion of sprint 1
scrum master: sprint 1 - all developers completed
scrum: sprint 2 for developer #2
developer #2: coding
scrum: developer #2,
 please wait for completion of sprint 2
manager: checking results of sprint 1
manager: waiting for completion of sprint 2
scrum: sprint 2 for developer #1
developer #1: coding
scrum: developer #1,
 please wait for completion of sprint 2
scrum master: sprint 2 - all developers completed
scrum master: bye-bye developer #1
manager: checking results of sprint 2
manager: bye-bye
scrum master: bye-bye developer #2






Last updated: July 16, 2012


Tuesday, July 27th, 2010
11:51 pm
Java 7 concurrency utilities: Face the Phaser, phase 2

image



        




Second code snippet in Phaser API documentation is not much more complicated than previous one, and it is quite easy to make an SSCCE to demonstrate it in more details. Way to "cause a set of threads to repeatedly perform actions for a given number of iterations..."


Some "actors" in the story for this code example are familiar by previous one. There are, again, several tasks, each running in its own thread ("developers doing coding"). This time though, we want tasks to repeat several times in a way similar to how developers do coding in Scrum sprints. This time, we will use the Phaser (the "scrum master") that controls the way how tasks get repeated.


Threads that execute tasks invoke (already familiar) Phaser's method arriveAndAwaitAdvance() prior to repeating running their tasks. This causes thread to wait until Phaser notifies it (until "all developers completed the sprint"). Prior to proceeding to next iteration, threads also invoke another Phaser's method isTerminated() to check if it's the time to stop iterating.




import java.util.Arrays;
import java.util.concurrent.Phaser;
import java.util.concurrent.atomic.AtomicInteger;

class FaceThePhaser2 {
    public static void main(String[] args) {
        new FaceThePhaser2().manageDevelopment();
    }

    public void manageDevelopment() {
        runDevelopment(Arrays.asList(
                new Developer(),
                new Developer()), 2);
    }

    // API doc Java 7 DRAFT ea-b102
    //  "One way to cause a set of threads to repeatedly perform actions
    //  for a given number of iterations is to override onAdvance"
    private void runDevelopment(Iterable<Developer> team, final int sprints) {
        log("scrum master: sprints: " + sprints);
        final Phaser scrumMaster = new Phaser() {
            @Override
            protected boolean onAdvance(int phase, int registeredParties) {
                log("scrum master: sprint " + (phase + 1)
                        + " - all developers completed");
                return phase >= sprints - 1 || registeredParties == 0;
            }
        };
        scrumMaster.register();
        for (final Developer developer : team) {
            scrumMaster.register();
            log("scrum master: welcome " + developer);
            new Thread() {
                @Override public void run() {
                    log("scrum master: run tasks for " + developer);
                    do {
                        final String sprint = describeSprint(scrumMaster);
                        log("scrum master: " + sprint + " for " + developer);
                        developer.run();
                        log("scrum master: " + developer
                                + ", please wait for completion of " + sprint);
                        scrumMaster.arriveAndAwaitAdvance();
                    } while (!scrumMaster.isTerminated());
                    log("scrum master: bye-bye " + developer);
                }
            }.start();
        }
        log("scrum master: welcoming complete");
        scrumMaster.arriveAndDeregister(); // deregister self, don't wait
    }

    private String describeSprint(Phaser scrumMaster) {
        final int phase = scrumMaster.getPhase();
        return phase < 0 ? "last sprint" : "sprint " + (phase + 1);
    }

    private static void log(String msg) { System.out.println(msg); }

    private static class Developer implements Runnable {
        private final static AtomicInteger idSource = new AtomicInteger();
        private final int id = idSource.incrementAndGet();
        public void run() { log(toString() + ": coding"); }
        @Override public String toString() { return "developer #" + id; }
    } // Developer
} // FaceThePhaser2


        
example output:
scrum master: sprints: 2
scrum master: welcome developer #1
scrum master: welcome developer #2
scrum master: run tasks for developer #1
scrum master: sprint 1 for developer #1
developer #1: coding
scrum master: welcoming complete
scrum master: developer #1,
 please wait for completion of  sprint 1
scrum master: run tasks for developer #2
scrum master: sprint 1 for developer #2
developer #2: coding
scrum master: developer #2,
 please wait for completion of  sprint 1
scrum master: sprint 1 - all developers completed
scrum master: sprint 2 for developer #2
developer #2: coding
scrum master: developer #2,
 please wait for completion of  sprint 2
scrum master: sprint 2 for developer #1
developer #1: coding
scrum master: developer #1,
 please wait for completion of  sprint 2
scrum master: sprint 2 - all developers completed
scrum master: bye-bye developer #1
scrum master: bye-bye developer #2


Above usage scenario, just like the previous one, is quite simple. In Java 5 and 6, similar job can be done with CyclicBarrier:




import java.util.Arrays;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicInteger;

class NoBigDeal2 {
    private volatile boolean sprintsCompleted = false;
    public static void main(String[] args) {
        new NoBigDeal2().manageDevelopment();
    }

    public void manageDevelopment() {
        runDevelopment(Arrays.asList(
                new Developer(),
                new Developer()), 2);
    }

    private void runDevelopment(List<Developer> team, final int sprints) {
        log("scrum master: sprints: " + sprints);
        final CyclicBarrier scrumMaster = new CyclicBarrier(team.size(),
                new Runnable() {
                    private int phase = 0;
                    public void run() {
                        if ( ++phase >= sprints) {
                            sprintsCompleted = true;
                        }
                        log("scrum master: sprint " + phase
                                + " - all developers completed");
                    }
                });
        for (final Developer developer : team) {
            log("scrum master: welcome " + developer);
            new Thread() {
                private int phase = 0;
                @Override public void run() {
                    log("scrum master: run tasks for " + developer);
                    do {
                        log("scrum master: sprint " + (++phase)
                                + " for " + developer);
                        developer.run();
                        log("scrum master: " + developer
                                + ", please wait for completion of  sprint "
                                + phase);
                        try {
                            scrumMaster.await();
                        } catch (InterruptedException ie) {
                            throw new RuntimeException(ie);
                        } catch (BrokenBarrierException bbe) {
                            throw new RuntimeException(bbe);
                        }
                    } while (!sprintsCompleted);
                    log("scrum master: bye-bye " + developer);
                }
            }.start();
        }
        log("scrum master: welcoming complete");
    }

    private static void log(String msg) { System.out.println(msg); }

    private static class Developer implements Runnable {
        private final static AtomicInteger idSource = new AtomicInteger();
        private final int id = idSource.incrementAndGet();
        public void run() { log(toString() + ": coding"); }
        @Override public String toString() { return "developer #" + id; }
    } // Developer
} // NoBigDeal2


        
example output:
scrum master: sprints: 2
scrum master: welcome developer #1
scrum master: welcome developer #2
scrum master: run tasks for developer #1
scrum master: sprint 1 for developer #1
developer #1: coding
scrum master: welcoming complete
scrum master: developer #1,
 please wait for completion of  sprint 1
scrum master: run tasks for developer #2
scrum master: sprint 1 for developer #2
developer #2: coding
scrum master: developer #2,
 please wait for completion of  sprint 1
scrum master: sprint 1 - all developers completed
scrum master: sprint 2 for developer #2
developer #2: coding
scrum master: developer #2,
 please wait for completion of  sprint 2
scrum master: sprint 2 for developer #1
developer #1: coding
scrum master: developer #1,
 please wait for completion of  sprint 2
scrum master: sprint 2 - all developers completed
scrum master: bye-bye developer #1
scrum master: bye-bye developer #2






Last updated: July 16, 2012


Thursday, July 1st, 2010
11:59 pm
Java 7 concurrency utilities: Face the Phaser, intro

image



        




Java 7 introduces a bunch of new APIs in util.concurrent package. Most notable one is a fork-join framework, explained in much details by Brian Goetz: Java theory and practice: Stick a fork in it, Part 2


Another new API is Phaser. Let's take a closer look at it. As the name suggests, it is intended to help arranging work in, well, phases - acting as kind of mediator


Below is an SSCCE based on the very first code snippet presented in Phaser API documentation. "Phaser may be used instead of a CountDownLatch to control a one-shot action serving a variable number of parties..."


The story for the code below is as follows. There are several tasks, each running in its own thread ("developers doing coding"). We want tasks to execute only after all their threads are started (after "all developers are assigned"). For that, we set up the Phaser (the "manager") that controls the way how tasks get started.


Threads that execute tasks invoke Phaser's method arriveAndAwaitAdvance() prior to running their tasks. This causes thread to wait until Phaser notifies it (until "all developers are assigned").


  • Side note. For a real life project, that would be rather lame way to manage it. On the other hand, there are real life projects that are managed in er less than perfect way so maybe our example is not that unrealistic afterall?



import java.util.Arrays;
import java.util.concurrent.Phaser;
import java.util.concurrent.atomic.AtomicInteger;

class FaceThePhaser {
    public static void main(String[] args) {
        new FaceThePhaser().manageDevelopment();
    }

    public void manageDevelopment() {
        runDevelopment(Arrays.asList(
                new Developer(),
                new Developer(),
                new Developer(),
                new Developer()));
    }

    // API doc Java 7 DRAFT ea-b102
    //  "Phaser may be used instead of a CountDownLatch  to control
    //  a one-shot action serving a variable number of parties.
    //  The typical idiom is for the method setting this up to first
    //  register, then start the actions, then deregister"
    private void runDevelopment(Iterable<Developer> team) {
        final int INIT_PARTIES = 1; // "1" to register self
        final Phaser manager = new Phaser(INIT_PARTIES);
        log("mgr: assign all developers, then start coding");
        for (final Developer developer : team) {
            final String dev = developer.toString();
            log("mgr: assigns a new unarrived " + dev + " to the project");
            manager.register();
            new Thread() {
                // todo rewrite this anonymous inner class
                //   after I figure how to enable closures
                @Override
                public void run() {
                    log("mgr: " + dev + ", please await all developers");
                    manager.arriveAndAwaitAdvance(); // await all creation
                    log("mgr: " + dev + ", OK to start coding");
                    developer.run();
                }
            }.start();
        }
        log("mgr: all assigned, start coding after all arrive");
        manager.arriveAndDeregister();
    }

    private static void log(String msg) { System.out.println(msg); }

    private static class Developer implements Runnable {
        private final static AtomicInteger idSource = new AtomicInteger();
        private final int id = idSource.incrementAndGet();
        public void run() { log(toString() + ": coding"); }
        @Override public String toString() { return "developer #" + id; }
    } // Developer
} // FaceThePhaser


        
example output:
mgr: assign all developers, then start coding
mgr: assigns a new unarrived
 developer #1 to the project
mgr: assigns a new unarrived
 developer #2 to the project
mgr: assigns a new unarrived
 developer #3 to the project
mgr: developer #1, please await all developers
mgr: assigns a new unarrived
 developer #4 to the project
mgr: developer #2, please await all developers
mgr: developer #4, please await all developers
mgr: all assigned, start coding after all arrive
mgr: developer #3, please await all developers
mgr: developer #3, OK to start coding
mgr: developer #4, OK to start coding
developer #4: coding
mgr: developer #2, OK to start coding
developer #2: coding
developer #3: coding
mgr: developer #1, OK to start coding
developer #1: coding


FaceThePhaser demonstrates a rudimentary simple scenario. Not surprisingly, pretty much the same can be accomplished with Java 5 and 6 API, using CountDownLatch:




import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

class NoBigDeal {
    public static void main(String[] args) {
        new NoBigDeal().manageDevelopment();
    }

    public void manageDevelopment() {
        runDevelopment(Arrays.asList(
                new Developer(),
                new Developer(),
                new Developer(),
                new Developer()));
    }

    private void runDevelopment(List<Developer> team) {
        // IMPL NOTE here, using Iterable would be less convenient
        //  than in FaceThePhaser - I'd have to calculate the size
        final CountDownLatch manager = new CountDownLatch(team.size());
        log("mgr: assign all developers, then start coding");
        for (final Developer developer : team) {
            final String dev = developer.toString();
            log("mgr: assigns a new unarrived " + dev + " to the project");
            new Thread() {
                @Override
                public void run() {
                    log("mgr: " + dev + ", please await all developers");
                    manager.countDown();
                    try {
                        manager.await();
                    } catch (InterruptedException ie) {
                        throw new RuntimeException(ie);
                    }
                    log("mgr: " + dev + ", OK to start coding");
                    developer.run();
                }
            }.start();
        }
        log("mgr: all assigned, start coding after all arrive");
    }

    private static void log(String msg) { System.out.println(msg); }

    private static class Developer implements Runnable {
        private final static AtomicInteger idSource = new AtomicInteger();
        private final int id = idSource.incrementAndGet();
        public void run() { log(toString() + ": coding"); }
        @Override public String toString() { return "developer #" + id; }
    } // Developer
} // NoBigDeal


        
example output:
mgr: assign all developers, then start coding
mgr: assigns a new unarrived
 developer #1 to the project
mgr: assigns a new unarrived
 developer #2 to the project
mgr: developer #1, please await all developers
mgr: assigns a new unarrived
 developer #3 to the project
mgr: developer #2, please await all developers
mgr: assigns a new unarrived
 developer #4 to the project
mgr: developer #4, please await all developers
mgr: developer #3, please await all developers
mgr: all assigned, start coding after all arrive
mgr: developer #3, OK to start coding
developer #3: coding
mgr: developer #2, OK to start coding
developer #2: coding
mgr: developer #4, OK to start coding
developer #4: coding
mgr: developer #1, OK to start coding
developer #1: coding


Phaser API documentation mentions that it is indeed similar in functionality to CyclicBarrier and CountDownLatch but supporting more flexible usage.






Last updated: July 16, 2012


Friday, June 4th, 2010
11:31 pm
Software Aging

image
We all know a mess when we see one, but too often we are in danger of being paralysed, unable to act on the evidence of our own senses because there is no procedural translation of It works but it's ugly. When an experienced professional feels aesthetical disquiet and cares enough to say so, we should always take notice. Our standards of beauty change from generation to generation, and for some reason always follow function. That is why making code beautiful exploits a huge knowledge base that we may not have consciously integrated, and leads to cost effective solutions. The stuff is less likely to incur vast maintenance costs downstream if it's beautiful. That's what beauty is. Aesthetical quality is probably the only criterion against which one can honesty argue that the wrong language has been used. An attempt to do an impressionist dawn in acrylic would be horrible even if the airbrush work were perfect.
(source: The Programmer's Stone)


image
  • Excessively complex (bloated) code is more complicated than it needs to be to accomplish its task. If rewritten, bloated code could become easier to understand and simpler to maintain. The nesting complexity of a line of code is the number of loops and conditionals enclosing it.
  • A history of frequent changes also known as code churn, suggests prior repairs and modifications. If change is inherently risky, then churn signifies decay.
  • Similarly, code with a history of code faults may be decayed, not only because of having been changed frequently, but also because fault fixes may not represent the highest quality programming.
  • Widely dispersed changes are a symptom of decay because changes to well-engineered, modularized code are local.
  • Kludges in code occur when developers knowingly make changes that could have been done more elegantly or efficiently. That kludged code will be difficult to change is almost axiomatic.
  • Numerous interfaces (i.e. entry points) are cited frequently by developers when they describe their intuitive definition of code decay. As the number of interfaces increases, increasing attention must be directed to possible side-effects of changes in other sections of code.
Eick et al. "Does Code Decay?", IEEE Transactions on Software Engineering, January 2001.
(source: Literate Programming)


Why does a system become a BIG BALL OF MUD? Sometimes, big, ugly systems emerge from THROWAWAY CODE. THROWAWAY CODE is quick-and-dirty code that was intended to be used only once and then discarded. However, such code often takes on a life of its own, despite casual structure and poor or non-existent documentation. It works, so why fix it? When a related problem arises, the quickest way to address it might be to expediently modify this working code, rather than design a proper, general program from the ground up. Over time, a simple throwaway program begets a BIG BALL OF MUD.

Even systems with well-defined architectures are prone to structural erosion. The relentless onslaught of changing requirements that any successful system attracts can gradually undermine its structure. Systems that were once tidy become overgrown as PIECEMEAL GROWTH gradually allows elements of the system to sprawl in an uncontrolled fashion.

If such sprawl continues unabated, the structure of the system can become so badly compromised that it must be abandoned. As with a decaying neighborhood, a downward spiral ensues. Since the system becomes harder and harder to understand, maintenance becomes more expensive, and more difficult. Good programmers refuse to work there. Investors withdraw their capital. And yet, as with neighborhoods, there are ways to avoid, and even reverse, this sort of decline. As with anything else in the universe, counteracting entropic forces requires an investment of energy. Software gentrification is no exception. The way to arrest entropy in software is to refactor it. A sustained commitment to refactoring can keep a system from subsiding into a BIG BALL OF MUD...

(source: Big Ball of Mud)
image




image

Subjecting convoluted code to scrutiny can set the stage for its... rehabilitation - that's so very true. Code reviews are really powerful tool.

I once was assigned to maintain a component in some project. Component was rather small (about 2K LOC if memory serves) and management considered it to be in "good enough" shape.

I spent about 2 or 3 months doing routine maintenance stuff - you know, studying code, fixing user complaints etc. It went rather painful but I thought it's because API and framework were totally new to me.

The moment of truth came when more experienced colleague was assigned to review the component. He did it quite thoroughly and ended up with about 180 comments. No kidding! there were about one hundred eighty points in his review - systematically listed, explained and justified points.

In front of an irrefutable evidence, management had to offer me a carte blanche to address the review points, no matter how much time it will take. I spent about a month working like crazy. Working like crazy hmm.... well there was certainly a lot of work but have to admit it was indeed less painful compared to what I did before.

Really, it was much easier to fix these systematically listed and professionally explained points than hassle with randomly picked, messed user complaints with their vague descriptions. I think that after I was done with review comments, most of the component code was reworked.

The rest of the story is really boring. User complaints and maintenance efforts dropped to almost zero after rework. Reworked component served flawlessly for several years after that, without major issues and refactorings. Boring, isn't it? wish all the products I work with were that boring. :)




Last updated: September 26, 2010



Sunday, May 30th, 2010
8:05 pm
workaround for javac bug 6302954 'no unique maximal instance exists for type variable'

image

'Good hunting!' said Phao (The Second Jungle Book)

Did recently code review for a friend's project. Design looked generally OK but I noticed a cast that was literally crying out loud generify this!

Before asking author why it was left hanging there, I decided to try to get rid of that cast myself. It turned out harder than I expected. Right when my code looked perfectly ready for removing the cast, compiler (JDK 6u19) began failing castless version:
no unique maximal instance exists for type variable

Hmmm. After some googling for the error message I discovered that problem is caused by known javac bug 6302954 Inference fails for type variable return constraint

Luckily, it turned out that there is a documented workaround for this bug. Below is SSCCE demonstrating the problem and the workaround:
interface Result {
  public static class Service {

    @SuppressWarnings("unchecked")
    public <T extends Result> T invokeWithCast() {
      // IMPL NOTE javac fails to compile without cast:
      //   no unique maximal instance exists for type variable
      //   T with upper bounds T,Result
      return (T)returnGenericType();
    }

    public <T extends Result> T invokeWithoutCast() {
      // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6302954
      return this.<T>returnGenericType();
    }

    private <T extends Result> T returnGenericType() { return null; }

  } // Service
} // Result

Basically, one just needs to explicitly specify type argument (JLS 12.15, explained in details in Langer's Generics FAQ).

image

Wednesday, May 12th, 2010
11:58 pm
Subversion - sucks, Mercurial / Git - rock?

image
Centralized _works_. It's just *inferior*. (Linus Torvalds)

Joel Spolsky:

...With distributed version control, merges are easy and work fine. image So you can actually have a stable branch and a development branch, or create long-lived branches for your QA team where they test things before deployment, or you can create short-lived branches to try out new ideas and see how they work.

image

This is too important to miss out on. This is possibly the biggest advance in software development technology in the ten years I’ve been writing articles here...

If you are using Subversion, stop it. Just stop. Subversion = Leeches. Mercurial and Git = Antibiotics. We have better technology now.

Antibiotics hmm. As far as I know, water chlorination is considered more efficient. :)

Related thoughts from Martin Fowler:

[Merge] ...may be just fine, the developers may have been working on completely separate parts of the code base with no interaction, in which case the merge will go smoothly. But they may be working on bits that do interact, in which case here lye dragons.

The dragons can come in many forms, and tooling can help slay some of them. The most of obvious dragon is the complexity of merging the source code and dealing with conflicts as developers edit the same files. Modern DVCSs actually handle this rather well, indeed somewhat magically. Git has quite the reputation for dealing with complicated merges. So much so that the textual issues of merging are much better than they used to be...

On the whole, however, I don't think cherry-picking with the VCS is a good idea.

Feature Branching is a poor man's modular architecture, instead of building systems with the ability to easy swap in and out features at runtime/deploytime they couple themselves to the source control providing this mechanism through manual merging.
--Dan Bodart

I much prefer designing the software in such a way that makes it easy to enable or disable features through configuration changes. My colleague Paul Hammant calls this Branch by Abstraction. This requires you to put some thought into what needs to be modularized and how to control that variation, but we've found the result to be far less messy that relying on the VCS...




With distributed version control, merges are easy and work fine... Interesting, this observation seems to match my own experience with another distibuted VCS - TeamWare. Doing merges with TeamWare felt indeed more comfortable than with Subversion.

image

Saturday, April 24th, 2010
12:01 pm
deadly sins in programming

image
"...Deadly Sins, also known as the Capital Vices or Cardinal Sins, is a classification of the most objectionable vices..." (Wikipedia)

image


thanks to the saint kind people at SDN forum for helping me to figure above list
Friday, April 9th, 2010
11:28 pm
Patterns for Concurrent and Networked Objects (POSA2)

image

"patterns... are most effective when they
enhance -- rather than impede -- communication
among software development team members"
(POSA2, page 531)



image

There once was a man who went to a computer trade show. Each day as he entered the man told the guard at the door: "I am a great thief, renowned for my feats of shoplifting. Be forewarned, for this trade show shall not escape unplundered."

This speech disturbed the guard greatly, because there were millions of dollars of computer equipment inside, so he watched the man carefully. But the man merely wandered from booth to booth, asking questions and humming quietly to himself.

When the man left, the guard took him aside and searched his clothes, but nothing was to be found. On the next day of the trade show, the man returned and chided the guard saying: "I escaped with a vast booty yesterday, but today will be even better." So the guard watched him ever more closely, but to no avail.

On the final day of the trade show, the guard could restrain his curiosity no longer. "Sir Thief", he said, "I am so perplexed, I cannot live in peace. Please enlighten me. What is it that you are stealing?"

The man smiled. "I am stealing patterns", he said.

(Adapted from "The TAO Of Programming")




Patterns overview


Based on patterns abstracts by Douglas Schmidt


 Service Access and Configuration Patterns
Wrapper Facade image

This design pattern encapsulates the functions and data provided by existing non-object-oriented APIs within more concise, robust, portable, maintainable, and cohesive object-oriented class interfaces.
     Real life example: Books consisting of edited collections of papers. Java: JVM, AWT


Component Configurator

This design pattern allows an application to link and unlink its component implementations at run-time without having to modify, recompile, or statically relink the application. Component Configurator further supports the reconfiguration of components into different application processes without having to shut down and re-start running processes.
     Real life example: football (soccer). Java: applets mechanism for life-cycle control.


Interceptor

This architectural pattern allows services to be added transparently to a framework and triggered automatically when certain events occur.
     Real life example: Change of address surface mail forwarding. Java: EJB (Interceptor Proxy variant).


Extension Interface image

This design pattern allows multiple interfaces to be exported by a component, to prevent bloating of interfaces and breaking of client code when developers extend or modify the functionality of the component.
     Java: EJB / JNDI (Asymmetric Extension Interface variant).


 Event Handling Patterns
Reactor

This architectural pattern allows event-driven applications to demultiplex and dispatch service requests that are delivered to an application from one or more clients.
     Real life example: Receiving phone calls. Java: event handling in in AWT (EDT)


Proactor

This architectural pattern allows event-driven applications to efficiently demultiplex and dispatch service requests triggered by the completion of asynchronous operations, to achieve the performance benefits of concurrency without incurring certain of its liabilities.
     Real life example: Phone call initiation via voice mail. Java: java.nio (JSR 51)


Asynchronous Completion Token

This design pattern allows an application to demultiplex and process efficiently the responses of asynchronous operations it invokes on services.
     Real life example: FedEx inventory tracking. Java: handback object in Jini.


Acceptor-Connector

This design pattern decouples the connection and initialization of cooperating peer services in a networked system from the processing performed by the peer services after they are connected and initialized.
     Real life example: Managers and secretaries.


 Synchronization Patterns
Scoped Locking

This C++ idiom ensures that a lock is acquired when control enters a scope and released automatically when control leaves the scope, regardless of the return path from the scope.
     Java: synchronized blocks


Strategized Locking

This design pattern parameterizes synchronization mechanisms that protect a component's critical sections from concurrent access.
     Java: java.util.concurrent.locks


Thread-Safe Interface

This design pattern minimizes locking overhead and ensures that intra-component method calls do not incur 'self-deadlock' by trying to reacquire a lock that is held by the component already.
     Real life example: Security checkpoints. Java: synchronization wrappers in Collections Framework


Double-Checked Locking Optimization image

This design pattern reduces contention and synchronization overhead whenever critical sections of code must acquire locks in a thread-safe manner just once during program execution.


 Concurrency Patterns
Active Object

This design pattern decouples method execution from method invocation to enhance concurrency and simplify synchronized access to objects that reside in their own threads of control.
     Real life example: Chef in a restaurant. Java: java.util.Timer, usage of Future in Executor


Monitor Object

This design pattern synchronizes concurrent method execution to ensure that only one method at a time runs within an object. It also allows an object's methods to cooperatively schedule their execution sequences.
     Real life example: Fast food restaurant. Java: each Java object can be a monitor object containing a monitor lock and a single monitor condition.


Half-Sync/Half-Async

This architectural pattern decouples asynchronous and synchronous service processing in concurrent systems, to simplify programming without unduly reducing performance. The pattern introduces two intercommunicating layers, one for asynchronous and one for synchronous service processing.
     Real life example: Restaurants.


Leader/Followers

This architectural pattern that provides an efficient concurrency model where multiple threads take turns sharing a set of event sources in order to detect, demultiplex, dispatch, and process service requests that occur on the event sources.
     Real life example: Airport taxi stands.


Thread-Specific Storage

This design pattern allows multiple threads to use one 'logically global' access point to retrieve an object that is local to a thread, without incurring locking overhead on each object access.
     Real life example: Local telephone directory services. Java: ThreadLocal




Real-life examples



  • Wrapper Facade: Books consisting of edited collections of papers. A real-life example of the Wrapper Facade pattern are books consisting of edited collections of papers that are organized into one or more 'themes'. For example, the PLoPD series consist of individual papers that are organized into cohesive sections, such as event handling, fault tolerance, application framework design, or concurrency. Thus, readers who are interested in a particular topic area or domain can focus their attention on these sections, rather than having to locate each paper individually.

  • Component Configurator: In football, which Americans call soccer, each team's coach can substitute a limited number of players during a match. The coach is the component configurator who decides which players to substitute, and the players embody the role of components. All players obey the same protocol with respect to substitution, which occurs dynamically, that is, the game does not stop during the substitutions. When players see a sign waved with their numbers, they leave the field and new players join the game immediately. The coach's list of the current 11 players corresponds to the Component Repository. Just as the reconfiguration script is not always written by the coach: some home crowds are renowned for asking and shouting for specific players to be put into the game—and for firing the coach.

  • Interceptor: Change of address surface mail forwarding. A real-life example of the Interceptor pattern arises when people move from one house to another. The post office can be instructed to intercept surface mail addressed to the original house and have it transparently forwarded to the new house. In this case, the contents of the mail is not modified and only the destination address is changed.

  • Reactor: Receiving phone calls. The Reactor pattern occurs frequently in everyday life, for example in telephony. Consider yourself as an event handler that registers with a reactor — a telecommunication network—to 'handle' calls received on a particular phone number — the handle. When somebody calls your phone number, the network notifies you that a 'call request' event is pending by ringing your phone. After you pick up the phone, you react to this request and 'process' it by carrying out a conversation with the connected party.

  • Proactor: Phone call initiation via voice mail. A real-life application of the Proactor pattern is the scenario in which you telephone a friend, who is currently away from her phone, but who returns calls reliably when she comes home. You therefore leave a message on her voice mail to ask her to call you back. In terms of the Proactor pattern, you are a initiator who invokes an asynchronous operation on an asynchronous operation processor—your friend's voice mail—to inform your friend that you called. While waiting for your friend's 'call-back' you can do other things, such as re-read chapters in POSA2. After your friend has listened to her voice mail, which corresponds to the completion of the asynchronous operation, she plays the proactor role and calls you back. While talking with her, you are the completion handler that 'processes' her 'callback'.

  • Asynchronous Completion Token: FedEx inventory tracking. An intriguing real-life example of the Asynchronous Completion Token pattern is implemented by the inventory tracking mechanism used by the US Federal Express postal services. A FedEx Airbill contains a section labeled: 'Your Internal Billing Reference Information (Optional: First 24 characters will appear on invoice).' The sender of a package uses this field as an ACT. This ACT is returned by FedEx (the service) to you (the initiator) with the invoice that notifies the sender that the transaction has completed. FedEx deliberately defines this field very loosely: it is a maximum of 24 characters, which are otherwise 'untyped.' Therefore, senders can use the field in a variety of ways. For example, a sender can populate this field with the index of a record for an internal database or with a name of a file containing a 'to-do list' to be performed after the acknowledgment of the FedEx package delivery has been received.

  • Acceptor-Connector: Managers and secretaries. A real-life implementation of the Acceptor-Connector pattern is often found in organizations that provide secretaries for their managers. A manager wishing to make a phone call to another manager asks her secretary to establish the call rather than doing it herself. However, the call is not received by the called manager directly, but by his secretary. Once the connection is established it is then passed to the managers. In terms of the Acceptor-Connector pattern, the secretary that initiates the phone call is the connector, the secretary that receives the call is the acceptor and the two managers are peer service handlers.

  • Thread-Safe Interface: Security checkpoints. You may encounter a real-life variation of the Thread-Safe Interface pattern when entering a country or commercial office building that has a security guard at the border or entrance. To be admitted, you must sign in. After being admitted, other people that you interact with typically trust that you are supposed to be there.

  • Active Object: Chef in a restaurant. A real-life example of the Active Object pattern is found in restaurants. Waiters and waitresses drop off customer food requests with the chef and continue to service requests from other customers asynchronously while the food is being prepared. The chef keeps track of the customer food requests via some type of worklist. However, the chef may cook the food requests in a different order than they arrived to use available resources, such as stove tops, pots, or pans, most efficiently. When the food is cooked, the chef places the results on top of a counter along with the original request so the waiters and waitresses can rendezvous to pick up the food and serve their customers.

  • Monitor Object: Fast food restaurant. A real-life example of the Monitor Object pattern occurs when ordering a meal at a busy fast food restaurant. Customers are the clients who wait to place their order with a cashier. Only one customer at a time interacts with a cashier. If the order cannot be serviced immediately, a customer temporarily steps aside so that other customers can place their orders. When the order is ready the customer re-enters at the front of the line and can pick up the meal from the cashier.

  • Half-Sync/Half-Async: Restaurants. Many restaurants use a variant of the Half-Sync/Half-Async pattern. For example, restaurants often employ a host or hostess who is responsible for greeting patrons and keeping track of the order in which they will be seated if the restaurant is busy and it is necessary to queue them waiting for an available table. The host or hostess is 'shared' by all the patrons and thus cannot spend much time with any given party. After patrons are seated at a table, a waiter or waitress is dedicated to service that table.

  • Leader/Followers: Taxi stands. The Leader/Followers pattern is used in everyday life to organize many airport taxi stands. In this use case, taxi cabs play the role of the 'threads,' with the first taxi cab in line being the leader and the remaining taxi cabs being the followers. Likewise, passengers arriving at the taxi stand constitute the events that must be demultiplexed to the cabs, typically in FIFO order. In general, if any taxi cab can service any passenger, this scenario is equivalent to the unbound handle/thread association described in the main Implementation section. However, if only certain cabs can service certain passengers, this scenario is equivalent to the bound handle/thread association described in the Variants section.

  • Thread-Specific Storage: Local telephone directory services. A real-life example of the Thread-Specific Storage pattern is found in telephone directory services. For example, in the United States, the 'logically global' number 411 can be used to connect with the local directory assistance operator for a particular area code or region.


Notes


  • Wrapper Facade more, concise, robust, portable, maintainable, cohesive... - isn't it covered by words object-oriented? Have to admit, I am not quite comfortable with above definition. I'd like it to more clearly reflect the way how Wrapper Facade "shields" the client code from complexities of different OS-specific APIs. I would rather prefer the wording like:
    This design pattern encapsulates the functions and data provided by different existing non-object-oriented APIs within more concise, robust, portable, maintainable, and cohesive uniform object-oriented class interfaces.
    I would also prefer the wording to better capture the difference between this pattern and Facade.


  • Extension Interface.
    'This pattern should be familiar to anyone who has programmed with Microsoft's Component Object Model (COM), Enterprise JavaBeans (EJB), or the CORBA Component Model (CCM), because it captures and generalizes the core concepts underlying these component technologies.'




  • Section 6.4 presents compelling explanation for why authors prefer organizing patterns as a language rather than classifying them in a pattern system.

A Pattern Language for Middleware and Applications


...we connect the patterns described in this book to a pattern language. This language is summarized in the following diagram. We recommend that you refer back to this diagram as you are reading the pattern language entries.

If a pattern uses another pattern in its implementation, it points to the pattern with an arrow. 'Duplicate' entries for patterns that are frequently referenced by other patterns avoid having too many crossed relationships. Architectural patterns are shaded to indicate where the language 'begins'.




image

"It has been our experience that organizing the patterns presented in this book as a pattern language is more effective than classifying them in a pattern system."
(section 6.4 Pattern Languages versus Pattern Systems) image



Friday, April 2nd, 2010
10:33 am
lazy initialization

image

Note: below assumes Java 5 or higher version

introduction

image

  • Lazy initialization

    In computer programming, lazy initialization is the tactic of delaying the creation of an object, the calculation of a value, or some other expensive process until the first time it is needed...


  • Singleton

    In software engineering, the singleton pattern is a design pattern that is used to restrict instantiation of a class to one object...


  • Double-checked locking (DCL)

    In software engineering, double-checked locking is a software design pattern also known as "double-checked locking optimization". The pattern is designed to reduce the overhead of acquiring a lock by first testing the locking criterion (the "lock hint") in an unsafe manner; only if that succeeds does the actual lock proceed.

    The pattern, when implemented in some language/hardware combinations, can be unsafe. It can therefore sometimes be considered to be an anti-pattern...


solutions


  • suggestion per online version of "Effective Java" (June 13, 2008):

    ...Simply make an enum type with one element:

    // Enum singleton - the preferred approach
    public enum Elvis {
       INSTANCE;
       public void leaveTheBuilding() { ... }
    }
    

    This approach is functionally equivalent to the public field approach, except that it is more concise, provides the serialization machinery for free, and provides an ironclad guarantee against multiple instantiation, even in the face of sophisticated serialization or reflection attacks. While this approach has yet to be widely adopted, a single-element enum type is the best way to implement a singleton...


  • recommendation per JMM FAQ (February 2004):

    ...Many people assumed that the use of the volatile keyword would eliminate the problems that arise when trying to use the double-checked-locking pattern. In JVMs prior to 1.5, volatile would not ensure that it worked (your mileage may vary). Under the new memory model, making the instance field volatile will "fix" the problems with double-checked locking, because then there will be a happens-before relationship between the initialization of the Something by the constructing thread and the return of its value by the thread that reads it.

    ...[note] volatiles are cheap on most platforms...

    Instead, use the Initialization On Demand Holder idiom, which is thread-safe and a lot easier to understand:

    private static class LazySomethingHolder {
      public static Something something = new Something();
    }
    public static Something getInstance() {
      return LazySomethingHolder.something;
    }
    

    This code is guaranteed to be correct because of the initialization guarantees for static fields; if a field is set in a static initializer, it is guaranteed to be made visible, correctly, to any thread that accesses that class.



  • overview at Joshua Bloch's JavaOne 2008 More Effective Java (pdf):

    What is the best technique for lazy initialization?

    • It depends...
      • Your default instinct should be normal (not lazy) initialization
      • To Break an Initialization Circularity, Use a Synchronized Accessor
        private FieldType field;
        synchronized FieldType getField() {
          if (field == null)
          field = computeFieldValue();
          return field;
        }
        
      • For High-Performance on a Static Field, use the Lazy Initialization Holder Class Idiom
        private static class FieldHolder {
          static final FieldType field = computeFieldValue();
        }
        static FieldType getField() {
          return FieldHolder.field;
        }
        
      • For High-Performance on an Instance Field, use the Double-Check Idiom
        private volatile FieldType field;
        FieldType getField() {
          FieldType result = field;
            if (result == null) { // 1st check (no lock)
              synchronized(this) {
              result = field;
              if (result == null) // 2nd check (w/ lock)
              field = result = computeFieldValue();
            }
          }
          return result;
        }

usage example


import java.util.concurrent.*;
class ElvisShmelvis {
    public enum Elvis {
        INSTANCE;
        { say("Elvis here"); }
        public void sing() { say("Love me tender"); }
    }
    public static void main(String[] args) {
        final int ELVIS_FANS_NUMBER = 3;
        Thread shalyapin = new Thread( new Runnable() {
            public void run () { say("Ha-ha, blokha!"); }
        });
        Thread[] elvisFans = new Thread[ELVIS_FANS_NUMBER];
        for (int i = 0; i < ELVIS_FANS_NUMBER; i++) {
            elvisFans[i] = new Thread (new Runnable() {
                public void run () {
                    say("Calling Elvis");
                    Elvis.INSTANCE.sing();
                }
            });
        }
        shalyapin.start();
        try { shalyapin.join(); }
        catch (InterruptedException ie) { say("ie: " + ie); }
        say("Shalyapin is gone");
        for (Thread fan: elvisFans) {
            fan.start();
        }
        for (Thread fan: elvisFans) {
            try { fan.join(); }
            catch (InterruptedException ie) { say("ie: " + ie); }
        }
        tellTheStory();
    }
    private static final BlockingQueue story
            = new LinkedBlockingQueue();
    private static final void say(String msg) {
        try { story.put(msg); }
        catch (InterruptedException ie) { /* swallow */ }
    }
    private static final void tellTheStory() {
        try { while (!story.isEmpty()) { System.out.println(story.take()); } }
        catch (InterruptedException ie) { /* swallow */ }
    }
}

  • example output
    Ha-ha, blokha!
    Shalyapin is gone
    Calling Elvis
    Calling Elvis
    Calling Elvis
    Elvis here
    Love me tender
    Love me tender
    Love me tender

    // java version "1.6.0_13"
    // Java(TM) SE Runtime Environment (build 1.6.0_13-b03)
    // Java HotSpot(TM) Client VM (build 11.3-b02, mixed mode, sharing))


background


  • Double-checked locking: Clever, but broken (02/09/2001)

    ...many well-meaning Java gurus encourage the use of the double-checked locking (DCL) idiom. There's only one problem with it – this clever-seeming idiom may not work...

    More accurately, DCL is not guaranteed to work. To understand why, we need to look at the relationship between the JVM and the computer environment on which it runs. In particular, we need to look at the
    Java Memory Model...

    One special case of lazy initialization that does work as expected without synchronization is the static singleton...


  • The "Double Checked Locking is broken" declaration (updated until about 2008)

    Double-Checked Locking is widely cited and used as an efficient method for implementing lazy initialization in a multithreaded environment.

    Unfortunately, it will not work reliably in a platform independent way when implemented in Java, without additional synchronization...

    If the singleton you are creating is static... Just define the singleton as a static field in a separate class. The semantics of Java guarantee that the field will not be initialized until the field is referenced, and that any thread which accesses the field will see all of the writes resulting from initializing that field...

    JDK5 and later extends the semantics for volatile so that the system will not allow a write of a volatile to be reordered with respect to any previous read or write, and a read of a volatile cannot be reordered with respect to any following read or write. See this entry in Jeremy Manson's blog for more details.

    With this change, the Double-Checked Locking idiom can be made to work by declaring the helper field to be volatile. This does not work under JDK4 and earlier...


  • Doug Lea analyzed the performance of some techniques for implementing lazy initialization (on 1.2.x JVMs)

    ...Eager, Volatile(DCL), ThreadLocal, SimThreadLocal, Synch, Thread Field, Direct Field...


Last updated: June 5, 2010


Saturday, March 20th, 2010
4:21 pm
who are you Mr Garbage Collector?

image

For the visually minded, here's a parable by Kieron Briggs:

image

Picture a big empty room with a big furnace/incinerator type thing at one end. Hanging form the roof are a number of ropes, called Threads. Attached to the various threads are little sparkly Objects, and those Objects can have other Object attached to them in turn by little rods called References. This creates a (hopefully) beautiful structure of Objects all attaches (either directly or indirectly) to a Thread. You can even have Objects which link to more than one Thread.

When an Object isn't needed any more, the Reference to it disappears. If that leaves the Object (or a whole collection of Objects) unattached to any Thread, it will fall down onto the floor and shatter. This gradually builds up a layer of broken objects lying around the floor. In a language like C, eventually the floor would become so full that there was no room for more Objects, and Bad Things would happen.

But in Java, we have a Garbage Collector. This is a little dude in overalls that climbs down a special Staff Only Thread, sweeps up all the broken Objects on the floor, and shovels them into the incinerator. You never know exactly when he's going to come along, but he's always there keeping an eye on the mess on the floor to make sure that it doesn't fill up too much...

Sunday, March 7th, 2010
2:34 pm
GWT App Architecture Best Practices

image

Google I/O presentation: Best Practices


[ << Previous 20 ]
About LiveJournal.com