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.

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.

"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.

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."

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.





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

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





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


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