Thread interference

Interleaving of multiple threads that manipulate shared data using multiple steps leads to thread interference. You can’t assume that a single statement of Java code executes atomically as a single step by the processor. For example, a simple statement like incrementing a variable value might involve multiple steps like loading of the variable value from memory to registers (working space), incrementing the value, and reloading the new value in the memory. When multiple threads execute this seemingly atomic statement, they might interleave, resulting in incorrect variable values.

Operations that use arithmetic and assignment operators like ++, --, +=, -=, *=, and /= aren’t atomic. Multiple threads that manipulate variable values using these operators can interleave.

Let’s work with an example of a class Book, which defines an instance variable copies- Sold that can be manipulated using its methods newSale() or returnBook():

The threads OnlineBuy and OnlineReturn manipulate the value of class Book’s instance variable copiesSold in their run() using methods newSale() and returnBook():

Let’s see what happens when another class, say, ShoppingCart, instantiates a Book and passes it to the threads OnlineBuy and OnlineReturn:

In the preceding code method main() starts three threads—task1, task2, and task3. These threads manipulate and share the same Book instance, book. The threads task1 and task2 execute book.newSale(), and task3 executes book.returnBook(). As mentioned previously, ++copiesSold and --copiesSold aren’t atomic operations. Also, as a programmer you can’t determine or command the exact time when these threads will start with their execution (it depends on how they’re scheduled to exe- cute by the OS). Let’s assume that task2 starts with its execution and reads book .copiesSold. Before it can modify this shared value, it’s also read by threads task3 and task2, which are unaware that this value is being modified by another thread. All these threads modify the value of book.copiesSold and update it back in order. The last thread to update the value book.copiesSold overrides updates of the other two threads. Figure below shows one of the possible ways in which the threads task1, task2, and task3 can interleave.

So, how can you assure that when multiple threads update shared values it doesn’t lead to incorrect results? How can you communicate between threads? Let’s discuss this in the next section.

Code Snipets:

1)

package com.gs.corejava.collections;

class Book {

	String title;
	int copiesSold = 0;

	Book(String title) {
		this.title = title;
	}

	public void newSale() {
		++copiesSold;
	}

	public void returnBook() {
		--copiesSold;
	}

}

class OnlineBuy extends Thread {
	Book book;

	public OnlineBuy(Book book) {
		this.book = book;
	}

	@Override
	public void run() {
		book.newSale();
	}

}

class OnlineReturn extends Thread {
	Book book;

	public OnlineReturn(Book book) {
		this.book = book;
	}

	@Override
	public void run() {
		book.returnBook();
	}

}

public class ShoppingCart {
	public static void main(String[] args) {
		Book book = new Book("Java");
		Thread onlineBuy1 = new OnlineBuy(book);
		Thread onlineBuy2 = new OnlineBuy(book);
		Thread onlineReturn = new OnlineReturn(book);

		onlineReturn.start();
		onlineBuy1.start();
		onlineBuy2.start();

		System.out.println(book.copiesSold);

	}
}

Output :

0 // sometimes 1

2)

package com.gs.corejava.collections;

class Book {

	String title;
	int copiesSold = 0;

	Book(String title) {
		this.title = title;
	}

	public void newSale() {
		int reg= copiesSold;
		reg++;
		copiesSold=reg;
	}

	public void returnBook() {
		int reg= copiesSold;
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		reg--;
		copiesSold=reg;
	}

}

class OnlineBuy extends Thread {
	Book book;

	public OnlineBuy(Book book) {
		this.book = book;
	}

	@Override
	public void run() {
		book.newSale();
	}

}

class OnlineReturn extends Thread {
	Book book;

	public OnlineReturn(Book book) {
		this.book = book;
	}

	@Override
	public void run() {
		book.returnBook();
	}

}

public class ShoppingCart {
	public static void main(String[] args) {
		Book book = new Book("Java");
		Thread onlineBuy1 = new OnlineBuy(book);
		Thread onlineBuy2 = new OnlineBuy(book);
		Thread onlineReturn = new OnlineReturn(book);

		onlineReturn.start();
		onlineBuy1.start();
		onlineBuy2.start();

		try {
			onlineBuy1.join();
			onlineBuy2.join();
			onlineReturn.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(book.copiesSold);

	}
}
1 // sometimes -1 or 0

Last updated