How to be more effective when pair programming

I have been pair programming for four months now. Although I have always been intrigued by this Agile practice and have theoretically understood its potential benefits, I was quite apprehensive about its effectiveness, never having tried it myself.

In the past months though, I have had the chance to experience pair programming firsthand, so I’d like to share my observations, lessons learnt and some do’s and don’ts.

Plan

Pair programming, by definition, is a collaborative activity between you and your ‘pair’. For effective collaboration, it is important to have a clear understanding of the goal and a general plan of action for the day’s tasks. The most common mistake is to immediately jump into programming without adequate thought or discussion on what needs to be done and how. This leads to confusion and waste of time. So the first step is to spend a few minutes making a roadmap for the day, before you begin coding. Here is a useful checklist to help get started:

  • What are we working on today?
  • What do we plan to achieve by the end of the day?
  • What was accomplished yesterday?
  • Are there blockers that need to be addressed first?
  • Are there personal constraints to be considered? (one of you needs to leave early, the other has a meeting to attend etc.,)

Synchronize

One of the things I had some trouble with in my early days of pairing was that my pair and I would often break for lunch, coffee etc., at different times. We ended up wasting time each day, waiting for each other during these breaks. So I decided to discuss my break plan (lunch, training, stroll etc.,) with my pair so that we could arrive at a common break schedule that worked for both of us. This way, we avoided waiting for each other and got more productive.

Pause

Don’t continue to work when your pair is not around. Don’t be that over zealous programmer who is too excited to stop work even for a few minutes when your pair has stepped away from your desk. This is an extremely insensitive behavior and can prevent your pair from effectively collaborating with you. So unless both of you have agreed otherwise, do not continue to work on a task individually, in the absence of either one of you.

It’s not Monopoly!

Keyboard monopolization – this is perhaps the number one complaint among pairing partners. Even if unintentional, this can get very annoying. If either one of you has more experience or story-background than the other, you may be tempted to hog the keyboard causing your pair to disengage completely. Next thing your pair will be opening up their own laptop and starting to check emails.

This may also happen when one of you is too shy to code (for example, your pair has just joined your team, your organization etc., ) In such cases, it is even more important that you consciously let them drive the work by turns. Sure, it can sometimes be difficult to restrain yourself. When you see your pair struggling with ‘extract method’ refactoring manually, and you think that a slight of your ‘defter’ hand could accomplish the same task within a split second. But remember, we are all new to tools and technologies at some point. Also, we all have our own individual style of learning and working. So, do avoid constant ‘helpful’ hints when your pair is at work. Do not interrupt. Do not cough keyboard shortcuts when your pair seems to be a bit slow. Instead, take a deep breath and watch them work (or learn, as the case might be). Encourage them to work at their own pace. A word of encouragement will go a long way in making your pair feel more comfortable working with you.
And if you are on the receiving end of this behavior – ‘Speak up!’ Tell your pair that you need some time to get more comfortable. There is no need to be apologetic. Don’t let your shyness or keyboard-discomfort become your excuse for not stepping up. Learn to switch driving seats with your pair and function as a peer.

Communicate

Like it or not, pairing is a social activity. It is your responsibility to embrace good social etiquette towards your pair. Constant interaction and collaboration with another individual can often get very stressful. It will take a lot of discipline and really good communication skills to make it less nerve-racking for the both of you. This is where most of us make mistakes, mostly because of years of mental conditioning and bad habits. Here are some tips that might help:

  • Communicate and share a plan (as already explained above)
  • Share your thoughts frequently. If you are stuck at a problem, step back, face your pair and tell them what you are thinking. Ask them what they think should be done.
  • Respect your pair’s opinions and skills. Pairing must remain a democratic process whereby skills and experience are used to collectively create something , and not become a competition where another individual is made to feel inferior. So engage your pair even when at times they may be feeling inhibited. And if you are the one on the receiving end, speak up. It is your responsibility to contribute.
  • Be courteous and polite. Ask for your pair’s opinions, and do not impose yours. Make suggestions. Discuss and argue over a point but don’t insist on having the last word, always. Even when you disagree with your pair, do so politely. Let your pair have an equal say in the matter.
  • Time-box all discussions. In other words, do not drag discussions for too long. One of the things that has often helped me is to say to my pair, “I don’t mind discussing this but since this is really a minor issue, let’s time-box it for 5 minutes”. This way, we can remain focussed and on-course all the time.
  • When your pair is coding, it is your job to take notes on things you think will need attention later. Do not interrupt or flag issues immediately. For example, if your pair has used a method name that you do not like for some reason, note it down. Do not break the flow. Once your pair is done with the typing, bring it up and discuss.
  • Vocalize frequently what was just done, what is being done and what is going to be done next. I know this sounds funny but here are two examples of situations where this helps me:
    • Catch errors and gaps if any, prior to checking in code: As we review each file before checking in our code, I say (out loud) things like, “ …So in this file we have now changed this function to throw an Exception…and then in this other file, we have added another constant, and then, we’ve refactored to alter the variable name here, here and here…but wait, why did we add this new constant here?”. This vocalization helps the pair of us to remain very alert to possible loopholes even before we check-in our code.
    • Pre-empt and analyze expected test results: While testing a piece of code manually, I take a moment to discuss the change we’ve just made, and pre-empt how this would affect the test results. Voicing out loud “…So we’ve changed the signature of this service to pass in a timezone value…that means when I click here, I would expect to see the product details with the correct time zone, right? …” This pre-emption helps to improve focus and effectiveness of the testing activity which can otherwise feel very repetitive and boring.

    The point is, the more you communicate, the better you get at it. There is of course the risk of over communicating and annoying your partner and I hope I am not doing that :-)

  • Mind your body language. A closed body language sends a negative message to your pair. Be mindful of that. Make sure you are always slightly turned towards your pair, always maintaining an acceptable social distance, even while typing.

Personal hygiene

There is a certain minimum standard of personal hygiene you must observe. Enough said.

Personality

Pairing with another programmer is like wine pairing. If attempting, it must be done just right.

And it’s all about sharing and learning . It need not always be predefined, uniform, or standardized. We all have our individual quirks and styles. Pairing should not suppress this. So let your own unique personality shine through. If you prefer a dark theme for your IDE (and if your pair is OK with it), go ahead and use a dark theme. If you like humming a peppy number while you wait for the code to compile, hum away. See if you can’t get your pair to drum on the desk in accompaniment.

Pairing is a great opportunity to learn something new about your co-workers. So love/hate other people’s unique quirks, thought processes, opinions, dress sense, eating habits…anything, but do not diss any of it. We all need to express ourselves, feel comfortable and make coming to work everyday worthwhile.

Pairing is fun but not a substitute for solo programming

Man, (they say) is a social animal. But to some of us, pairing can feel like this.

Personally, I need to work alone a few hours every week. It’s a different world altogether and I enjoy my solo flights very much. If you are like me, try to fit in a few hours of solo programming into your routine. This will help improve your own zen and your pair’s lifespan.

And therefore…

Besides being a useful productivity booster, pair programming (when done right) can prove to be a rewarding experience.

Pair programming is similar to Shavasana, in that it looks deceptively simple. Yoga experts however deem it one of the most difficult asanas to master.

                                                                                                                                                                                                                                                                                                                                                                 

Algorithm practice – Possible addition combinations for any given number

I came across this interesting puzzle today on stackoverflow. Basically, the problem is to display all possible combinations of additions that can sum up to a given number. so for example, if the user enters 6, the program should display

>> 6

0+6=6
1+1+1+1+1+1=6
1+1+1+1+2=6
1+1+1+3=6
1+1+4=6
1+5=6
2+1+1+1+1=6
2+1+1+2=6
2+1+3=6
2+4=6
3+1+1+1=6
3+1+2=6
3+3=6
4+1+1=6
4+2=6
5+1=6
6+0=6

This is a classic recursion problem. Here’s my solution in Java.

	public void run(int n)
	{

		List<StringBuilder> combos = showAdditionsFor(n);
		
		for (StringBuilder s : combos)
		{
			if (s.indexOf("+") < 0)
			{
				System.out.println(s + " + 0 = " + n);
				System.out.println("0 + " + s + " = " + n);
			}
			else
			{
				System.out.println(s + " = " + n);
			}
		}
	}
	
	List<StringBuilder> showAdditionsFor(int n)
	{
		List<StringBuilder> list = new ArrayList<StringBuilder>();
		if (n == 0)
			list.add(new StringBuilder(""));
		else if (n == 1)
			list.add(new StringBuilder(String.valueOf(1)));
		else
		{
			for (int i = 1; i <=n; i++)
			{
				//get n-i list
				List<StringBuilder> tempList = showAdditionsFor(n-i);
				appendToEachListElement(String.valueOf(i),tempList);
				list.addAll(tempList);
			}
		}
			
		return list;
	}
	
	
	private void appendToEachListElement(String x, List<StringBuilder>l)
	{
		for (StringBuilder s : l)
		{
			if (s.length() == 0)
				s.append(x);
			else
				s.append("+" + x);
		}
	}

My program creates a list of all possible combinations as Strings. For input=6, it prints all possible combinations as follows.


1+1+1+1+1+1 = 6
2+1+1+1+1 = 6
1+2+1+1+1 = 6
3+1+1+1 = 6
1+1+2+1+1 = 6
2+2+1+1 = 6
1+3+1+1 = 6
4+1+1 = 6
1+1+1+2+1 = 6
2+1+2+1 = 6
1+2+2+1 = 6
3+2+1 = 6
1+1+3+1 = 6
2+3+1 = 6
1+4+1 = 6
5+1 = 6
1+1+1+1+2 = 6
2+1+1+2 = 6
1+2+1+2 = 6
3+1+2 = 6
1+1+2+2 = 6
2+2+2 = 6
1+3+2 = 6
4+2 = 6
1+1+1+3 = 6
2+1+3 = 6
1+2+3 = 6
3+3 = 6
1+1+4 = 6
2+4 = 6
1+5 = 6
6 + 0 = 6
0 + 6 = 6

The program could also be further refined to eliminate duplicate combinations by using Sets instead of StringBuilders.

I have also decided to eliminate the combination with 0 in the calculation logic because it adds ambiguities to the solution (as this answer points out). Instead I am handling this case by brute force in the run method.

A possible reason why database transactions may not work in CodeIgniter with MySQL

For CodeIgniter transactions to work in MySQL, make sure that the database engine is InnoDB or BDB, both of which are transactional as opposed to MyISAM which is more common but a non-transactional engine.

This is clearly mentioned in the CodeIgniter documentation but easy to miss if you are not paying attention.

Vi magic

I was trying to replace the pipe character with a comma in a file using vi. I tried using

:%s/\|/,/g

to replace all pipes with commas in the following string.

|a|b|c|d

But this didn’t work and resulted in a comma before each letter as follows.

,|,a,|,b,|,c,|,d

Searching on the internet, I found a suggestion that worked.

:%s/\v\|/,/g

this correctly replaced the pipes to commas as expected.

a,b,c,d

After some digging through the vi manual, I found out that this was because vi has a special meaning for ‘\|’ when used in patterns.

In a vi substitution pattern, some characters are taken literally and gain special meaning if preceded by a backslash. At the same time, there are some other characters that have a special meaning by default and need to be preceded by a backslash for a literal match.

This is governed by the ‘magic’ option in vi.

The pipe character is non-magic by default which means it need not be escaped for a literal search. So the expression

:%s/|/,/g

would have also worked in this case.

The expression

:%s/\|/,/g

escaped the pipe character and therefore made it “magic”. Pipe, in its special form is treated as a separator. So in this case, the expression reads

“Find nothing and replace with a comma i.e., add a comma before every character.”

Using \v makes every following character to be treated as special or magical. Therefore

:%s/\v|/,/g

makes the pipe character from non-magical to magical.

But escaping pipe with a slash makes it non-magical again.

:%s/\v\|/,/g

The expression, therefore, now reads as

“Replace pipe character with comma”

which works as intended.

Mars Rovers

This is my solution for the famous Mars Rovers problem. The problem is a typical random walk simulation problem with input data coming from user. I wrote this in Java in 2007. Looking back, I do admit that there is a lot of verbosity here. In a more expressive language, the solution could be reduced to half, I am sure.

package com.nasa.rovers.data;

//Enumerator for Directions (N,E, W, S)
/*
 * Each direction is defined as a set of 1 step move from (0,0) in that direction
 * so NORTH is (0,1) and EAST is (1,0) and SOUTH is (0,-1) and so on
 *
 */
public enum DIR {
	N(0, 1), //NORTH
	E(1, 0), //EAST
	S(0, -1), //SOUTH
	W(-1, 0); //WEST

	private XY xy;

	DIR(int x, int y) {
		xy = new XY(x, y);
	}

	public XY getXY() {
		return xy;
	}

	public DIR left() {
		//return the left (i.e. anti clockwise) move of 'this' direction
		switch (this) {
		case N:
			return W;
		case E:
			return N;
		case S:
			return E;
		case W:
			return S;
		}

		throw new AssertionError("Unknown direction: " + this);
	}

	public DIR right() {
		//return the right (i.e. clockwise) move of 'this' direction
		switch (this) {
		case N:
			return E;
		case E:
			return S;
		case S:
			return W;
		case W:
			return N;
		}

		throw new AssertionError("Unknown direction: " + this);
	}

}

package com.nasa.rovers.data;

/*
 * Encapsulation for (x,y) coordinates
 * Needed because they are everywhere.
 */
public class XY
{

	private int x = 0;
	private int y = 0;

	public XY() {};

	public XY(int x, int y)
	{
		this.x=x;
		this.y=y;
	}

	public XY(XY xy)
	{
		this.x = xy.x;
		this.y = xy.y;
	}

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}

	public void add(XY xy)
	{
		this.x = this.x + xy.getX();
		this.y = this.y + xy.getY();

	}

	public void scale(int m)
	{
		this.x *= m;
		this.y *= m;
	}

	public String toString()
	{
		return "(" + x + "," + y + ")";
	}

	public static XY add(XY from, XY to)
	{
		XY result = new XY(from);
		result.add(to);
		return result;
	}

}

package com.nasa.rovers.data;

import java.util.ArrayList;
import java.util.List;

//Define the boundaries of the surface
public class Map {

	private XY left  = new XY(0,0);
	private XY right = left;

	private int maxX = right.getX();
	private int maxY = right.getY();

	public int getMaxX() {
		return maxX;
	}

	public int getMaxY() {
		return maxY;
	}

    public Map() {};

	public Map(XY right)
	{
		this.right = new XY(right);

		maxX = right.getX();
		maxY = right.getY();
	}

	public void setBoundary(XY right)
	{
		this.right = new XY(right);
		maxX = right.getX();
		maxY = right.getY();
	}

	/*
	 * Check if the coordinates are on the map or outside
	 */
	public boolean onMap(XY xy)
	{
		if ((0 <= xy.getX() && xy.getX() <= maxX) &&
		   (0 <= xy.getY() && xy.getY() <= maxY))
			return true;
		else
			return false;
	}

}

package com.nasa.rovers.data;

public class Rover
{

	private XY xy;
	private DIR dir;

	private boolean debug = Boolean.getBoolean("debug");

	public Rover() {};

	public Rover(int x, int y, DIR d)
	{
		xy = new XY(x,y);
		this.dir = d;
	}

	public XY getXY() {
		return xy;
	}

	public void setXY(XY xy) {
		this.xy = xy;
	}

	public DIR getDir() {
		return dir;
	}

	public void setDir(DIR dir) {
		this.dir = dir;
	}

	public void move()
	{
		xy.add(dir.getXY());
	}

	public void moveBy(int m)
	{
		XY moveBy = new XY(dir.getXY());
		moveBy.scale(m);
		xy.add(moveBy);
	}

	public void leftSpin()
	{
		this.dir = this.dir.left();
	}

	public void rightSpin()
	{
		this.dir = this.dir.right();
	}

    /*
     * Heart of the system
     * Navigates based on plan (string of instruction characters) and
     * a map that defines the boundaries
     * So...
     * The rover knows where it is going
     *
     * TODO implement logic to avoid collision with other rovers
     */
	public void navigate(String plan, Map map)
	{
		//must check if boundaries are crossed
		//rover must have knowledge of planet (map)

		char[] instructions = plan.toCharArray();
		char ins = 0;

		int moveX = 0;
		int moveY = 0;
		XY moveTo = null;

		print("start");
		print(this);

		for (int i =0; i < instructions.length; i++)
		{
			print(instructions[i]);
			switch (instructions[i])
			{
			case 'L' : leftSpin();break;
			case 'R' : rightSpin(); break;
			case 'M' :
				//check if it's a valid move
				//a move is invalid if it takes the rover beyond the boundary

				//find out what the final destination will be
				moveTo = XY.add(this.xy, this.dir.getXY());

				if (map.onMap(moveTo))
				{
					//safe move as it doesn't take us out of the boundaries
					move();
				}
				else
				{
					//don't move
					//should we throw an exception?
					print("invalid move");
				}
				break;

			default: //do nothing; we just ignore all crappy instructions.
			}

			print(this);
		}

		System.out.println(this);
		print("end");
	}

	public String toString()
	{
		return xy.toString() + "  " + dir.toString();
	}

	private void print(Object o)
	{
		if (debug) System.out.println(o);
	}
}

package com.nasa.rovers.control;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;

import com.nasa.rovers.data.DIR;
import com.nasa.rovers.data.Map;
import com.nasa.rovers.data.Rover;
import com.nasa.rovers.data.XY;

public class Navigator
{

	public static void main(String[] args)
	{
		System.exit(run(args));
	}

	public static int run(String[] args)
	{
		System.out.println("---Begin Program");

		try
		{

			Map map = new Map();

			String fileName = args[0]; //do basic authentication later

			//read input data
			BufferedReader f = new BufferedReader(new FileReader(fileName));
		    String line = null;

		    //read first line
		    line = f.readLine();
		    if (line != null)
		    {
		    	//top right coordinates
		    	line = line.trim();
		    	int x = Integer.parseInt(line.substring(0,1));
		    	int y = Integer.parseInt(line.substring(2));
		    	map.setBoundary(new XY(x,y));
		    }

		    int i = 1;
		    Rover r = null;
		    //TODO should we maintain a list of rovers
		    while (( line = f.readLine()) != null)
		    {
		    	line = line.trim();
		    	if (i % 2 != 0)
		    	{
		    		// first line of input for rover
			    	int x = Integer.parseInt(line.substring(0,1));
			    	int y = Integer.parseInt(line.substring(2,3));

			    	char d = line.charAt(4);
			    	switch (d)
			    	{
			    	case 'N': r = new Rover(x,y, DIR.N);break;
			    	case 'E': r = new Rover(x,y, DIR.E);break;
			    	case 'S': r = new Rover(x,y, DIR.S);break;
			    	case 'W': r = new Rover(x,y, DIR.W);break;
			    	default : r = new Rover(x,y, DIR.N);break; //create a new Rover anyway
			    	}

		    	}
		    	else
		    	{
		    		// second line of input
		    	    //TODO implement logic to avoid collision with other rovers

		    		r.navigate(line, map);
		    	}

		    	i++;

		    }

		}
		catch(Exception e)
		{
			e.printStackTrace();
			return (-1);
		}

		System.out.println("---End Program");
		return (0);
	}

}

SQL – Nuances of IsNumeric() function

I love complex sql queries.

Today I was working on generating a report that called for a join between two tables which looked as those below. The fist table (company) contained approx.80K records and the second (company_lookup) contained approx. 20K records.


.--------------------------------.
|             Company            |
+----+------------------+--------+
| Id | Name             | Ticker |
+----+------------------+--------+
|  1 | Dummy company 1  | IBM    |
|  2 | Dummy company 2  |   0002 |
|  3 | Dummy company 3  | 46ABD  |
|  4 | Dummy company 4  | NAN    |
|  5 | Dummy company 5  | BP     |
|  6 | Dummy company 6  | BP     |
|  7 | Dummy company 7  | MSFT   |
|  8 | Dummy company 8  | E      |
|  9 | Dummy company 9  | GOOG   |
| 10 | Dummy company 10 | SUN    |
| 11 | Dummy company 11 |   0046 |
‘----+------------------+--------’


.---------------------------.
|       Company_Lookup      |
+------------------+--------+
| Name             | Ticker |
+------------------+--------+
| Dummy company 1  | IBM    |
| Dummy company 2  |      2 |
| Dummy company 3  | 46ABD  |
| Dummy company 4  | NAN    |
| Dummy company 5  | BP     |
| Dummy company 7  | MSFT   |
| Dummy company 8  | E      |
| Dummy company 9  | GOOG   |
| Dummy company 11 |     46 |
‘------------------+--------’

As there can be multiple records with the same ticker, I tried writing a simple join on ticker and name.

select * from company a, company_lookup b
where a.ticker = b.ticker and
         a.name = b.name

However, notice the following rows


.--------------------------------.
|             Company            |
+----+------------------+--------+
| Id | Name             | Ticker |
+----+------------------+--------+
|  1 | Dummy company 1  | IBM    |
|  2 | Dummy company 2  |   0002 |  <-----
|  3 | Dummy company 3  | 46ABD  |
|  4 | Dummy company 4  | NAN    |
|  5 | Dummy company 5  | BP     |
|  6 | Dummy company 6  | BP     |
|  7 | Dummy company 7  | MSFT   |
|  8 | Dummy company 8  | E      |
|  9 | Dummy company 9  | GOOG   |
| 10 | Dummy company 10 | SUN    |
| 11 | Dummy company 11 |   0046 |  <-----
‘----+------------------+--------’

.---------------------------.
|       Company_Lookup      |
+------------------+--------+
| Name             | Ticker |
+------------------+--------+
| Dummy company 1  | IBM    |
| Dummy company 2  |      2 |   <-----
| Dummy company 3  | 46ABD  |
| Dummy company 4  | NAN    |
| Dummy company 5  | BP     |
| Dummy company 7  | MSFT   |
| Dummy company 8  | E      |
| Dummy company 9  | GOOG   |
| Dummy company 11 |     46 |   <-----
‘------------------+--------’

Essentially, the ticker is same but is stored differently in the two tables (although the data type is varchar in both tables). A simple join doesn’t work as it fails to compare, for ex., ’2′ with ’0002′. I can’t blindly convert the column to numeric either since the column also stores alphanumeric values. After a lot of tinkering, I managed to write a rather ugly query using ‘isnumeric’ and ‘case’ clause.

select b.*, a.* from company a, company_lookup b
where
case isnumeric(a.ticker)
    when 0 then a.ticker
    when 1 then cast(cast(a.ticker as int) as varchar(10))  --stripping leading zeros, if any, by converting to int and then back to varchar
end = b.ticker
and
b.name = a.name

My intention was to do a numeric conversion only if the tickers are numeric. To my dismay, this query didn’t work either and kept giving me a ‘Can’t convert NaN to varchar’ error. Upon debugging further, it turned out that the isNumeric() function treats ‘NaN’, ‘E’, ’31E’ etc. as numeric. However, the cast/convert idn’t able to convert ‘NAN’ to a valid numeric value.

This issue has also been touched upon in the following stackoverflow query: http://stackoverflow.com/questions/570075/sql-isnumeric-returns-true-but-sql-reports-conversion-failed

So, finally I had to modify the query to filter out such values as follows.

select b.*, a.* from company a, company_lookup b
where
case a.ticker
    when 'NAN' then 'NAN'
    when 'E' then 'E'
else
    case isnumeric(a.ticker)
        when 0 then a.ticker
        when 1 then cast(cast(a.ticker as int) as varchar(10))
    end
end = b.ticker
and
b.name = a.name

P.S. – By the way, the above tables have been generated using the Text::ASCIITable perl module. Also, WordPress has a habit of converting double dashes to some special character so I had to edit the HTML of this blog entry to replace all dashes with ampersand hash045;

Java Thread Programming by Paul Hyde

I have just finished reading “Java Thread Programming” by Paul Hyde. I have never gotten my hands dirty with concurrent programming in java and it is a matter of great consternation and distress to me every time I need to write multithreaded code or go for a job interview (For some reasons, multithreading seems to be a favorite trivia topic with interviewers). I know the basics but not enough to call myself even intermediately skilled in it. Not that I haven’t tried. But every time I picked up a book on multithreading, I found myself helplessly entangled in the confusing Java Swing examples the author had used to explain esoteric concurrency concepts. I dislike Swing. I have never used it. And I could never find a single book that explained multithreading with simple server side code snippets alluding never or rarely to Swing examples. Until I found the aforementioned book by Paul Hyde. This is the first multithreading book that I thoroughly enjoyed for its simple, easy to understand language.

The book is divided into two parts. Part I contains ten short yet lucidly written chapters covering the basic concepts from how to create a thread to more complex topics like thread intercommunication. Part II of the book is dedicated to useful tips and tricks like thread pooling etc.

Paul has used simple code examples in most of the places and has kept Swing/AWT examples to a minimum. Instead of peppering Swing code everywhere, he has written a separate chapter explaining how multithreading can be used in Java GUI programming. Personally I found this to be the best introductory book on Java multithreading. It is the unofficial “Head First…” book of java multithreading. You can download the source code from the book’s official web page.

This book was published in 1999 and therefore does not cover the new concurrent APIs introduced in Java 5. But if you scratch your head every time you come across a synchronization block, this is the book for you. Specially if you, like me, get confused and lost in the labyrinthian Swing examples that most books on this subject contain.

Thanks, Paul. I now feel well equipped to start hacking concurrent programming.

Follow

Get every new post delivered to your Inbox.

Join 34 other followers