Protect shared data

Threads are lightweight processes that share certain areas of the memory, unlike regular processes. This makes threads very efficient, but it also introduces additional complexities regarding memory management. You can protect your shared data by making it accessible (for reading and writing) to only one thread at a point of time. Other techniques include defining immutable classes (the states of which can’t be modified and defining volatile variables.

Identifying shared data:

A JVM instance hosts only one Java application. A Java application can create and execute multiple threads. When a new thread is created, it’s allotted its exclusive share in the memory. The JVM also allows partial runtime data to be shared between threads. A JVM’s runtime data set includes the method area, the heap, Java stacks, and PC registers (it also includes native method stacks, which aren’t covered by the exam, so they aren’t discussed here). The method area includes class information that includes a note on all its variables and methods. This data set includes the static variables, which are accessible and shared by all the threads in a JVM. The static vari- ables are shared by one class loader—that is, each class loader has its own set of static variables. The heap includes objects that are created during the lifetime of an application, again shared by multiple threads and processes. PC registers and Java stacks aren’t shared across the threads. On instantiation, each thread gets a PC regis- ter, which defines the next set of instructions to execute. Each thread is also assigned a Java stack. When a thread begins execution of a method, a method frame is created and inserted into the java stack, which is used to store the local variables, method parameters, return values, and intermediate values that might be used in a method.

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():

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.

Examples:

1)

import java.util.Set;

public class Thread1 {
	public static void main(String[] args) {

		StringBuilder string = new StringBuilder("shared ");

		Thread thread = new ProducerThread(string);
		Thread thread2 = new ConsumerThread(string);

		thread.start();
		thread2.start();

		Set<Thread> seThreads = Thread.getAllStackTraces().keySet();

		seThreads.stream().map(Thread::getName).forEach(System.out::println);

		try {
			thread.join();
			thread2.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		System.out.println(string);
	}
}

class ProducerThread extends Thread {
	private StringBuilder string = null;

	public ProducerThread(StringBuilder string) {
		this.string = string;
	}

	@Override
	public void run() {
		System.out.println("I am prod thread");
		int i = 0;
		while (i < 100) {
			i++;
			string.append("prod " + i);
		}
	}
}

class ConsumerThread extends Thread {

	private StringBuilder string = null;

	public ConsumerThread(StringBuilder string) {
		this.string = string;
	}

	@Override
	public void run() {
		System.out.println("I am con thread");
		int i = 0;
		while (i < 100) {
			i++;
			string.append("cons " + i);
		}
	}
}
I am prod thread
I am con thread
Thread-1
Signal Dispatcher
Finalizer
Attach Listener
main
Thread-0
Reference Handler
shared prod 1prod 2prod 3prod 4prod 5prod 6prod 7prod 8prod 9prod 10prod 11prod 12prod 13prod 14prod 15prod 16prod 17prod 18prod 19prod 20prod 21prod 22prod 23prod 24prod 25prod 26prod 27prod 28prod 29prod 30prod 31prod 32prod 33prod 34prod 35prod 36prod 37prod 38prod 39prod 40prod 41prod 42prod 43prod 44prod 45cons 1cons 2cons 3prod 46cons 4cons 5cons 6cons 7cons 8prod 47cons 9cons 10prod 48

2)

public class Thread1 {
	public static void main(String[] args) {

		MyStringBuilder string = new MyStringBuilder(new StringBuilder("shared "));

		Thread thread = new ProducerThread(string);
		Thread thread2 = new ConsumerThread(string);

		thread.start();
		thread2.start();

		try {
			thread.join();
			thread2.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		System.out.println(string);
	}
}

class MyStringBuilder {
	StringBuilder string;

	MyStringBuilder(StringBuilder string) {
		this.string = string;
	}

	public void setString(StringBuilder string) {
		this.string = string;
	}

	public StringBuilder getString() {
		return string;
	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return string.toString();
	}
}

class ProducerThread extends Thread {
	private MyStringBuilder string = null;

	public ProducerThread(MyStringBuilder string) {
		this.string = string;
	}

	@Override
	public void run() {
		System.out.println("I am prod thread");
		int i = 0;
		while (i < 100) {
			i++;
			string.getString().append("prod " + i);
		}
	}
}

class ConsumerThread extends Thread {

	private MyStringBuilder string = null;

	public ConsumerThread(MyStringBuilder string) {
		this.string = string;
	}

	@Override
	public void run() {
		System.out.println("I am con thread");
		int i = 0;
		while (i < 100) {
			i++;
			string.getString().append("cons " + i);
		}
	}
}
I am prod thread
I am con thread
shared prod 1prod 2prod 3prod 4prod 5prod 6prod 7prod 8prod 9prod 10prod 11prod 12prod 13prod 14prod 15prod 16prod 17prod 18prod 19prod 20prod 21prod 22prod 23prod 24prod 25prod 26prod 27prod 28prod 29prod 30prod 31prod 32prod 33cons 1prod 34cons 2prod 35cons 3prod 36cons 4cons 57

3) With synchronzation (On object):

import java.util.Set;

public class Thread1 {
	public static void main(String[] args) {

		StringBuilder string = new StringBuilder("shared ");

		Thread thread = new ProducerThread(string);
		Thread thread2 = new ConsumerThread(string);

		thread.start();
		thread2.start();

		Set<Thread> seThreads = Thread.getAllStackTraces().keySet();

		seThreads.stream().map(Thread::getName).forEach(System.out::println);

		try {
			thread.join();
			thread2.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		System.out.println(string);
	}
}

class ProducerThread extends Thread {
	private StringBuilder string = null;

	public ProducerThread(StringBuilder string) {
		this.string = string;
	}

	@Override
	public void run() {
		System.out.println("I am prod thread");
		synchronized (string) {
			int i = 0;
			while (i < 100) {
				i++;
				string.append("prod " + i);
			}
		}
	}
}

class ConsumerThread extends Thread {

	private StringBuilder string = null;

	public ConsumerThread(StringBuilder string) {
		this.string = string;
	}

	@Override
	public void run() {
		System.out.println("I am con thread");
		synchronized (string) {
			int i = 0;
			while (i < 100) {
				i++;
				string.append("cons " + i);
			}
		}
	}
}

4)

public class Thread1 {
	public static void main(String[] args) {

		MyStringBuilder string = new MyStringBuilder(new StringBuilder("shared "));

		Thread thread = new ProducerThread(string);
		Thread thread2 = new ConsumerThread(string);

		thread.start();
		thread2.start();

		try {
			thread.join();
			thread2.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		System.out.println(string);
	}
}

class MyStringBuilder {
	StringBuilder string;

	MyStringBuilder(StringBuilder string) {
		this.string = string;
	}

	public void setString(StringBuilder string) {
		this.string = string;
	}

	public synchronized StringBuilder getString() {
		return string;
	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return string.toString();
	}
}

class ProducerThread extends Thread {
	private MyStringBuilder string = null;

	public ProducerThread(MyStringBuilder string) {
		this.string = string;
	}

	@Override
	public void run() {
		System.out.println("I am prod thread");
		int i = 0;
		while (i < 100) {
			i++;
			string.getString().append(" prod" + i);
		}
	}
}

class ConsumerThread extends Thread {

	private MyStringBuilder string = null;

	public ConsumerThread(MyStringBuilder string) {
		this.string = string;
	}

	@Override
	public void run() {
		System.out.println("I am con thread");
		int i = 0;
		while (i < 100) {
			i++;
			string.getString().append("cons" + i);
		}
	}
}

5)

public class Thread1 {
	public static void main(String[] args) {

		MyStringBuilder string = new MyStringBuilder(new StringBuilder("shared "));

		Thread thread = new ProducerThread(string);
		Thread thread2 = new ConsumerThread(string);

		thread.start();
		thread2.start();

		try {
			thread.join();
			thread2.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		System.out.println(string);
	}
}

class MyStringBuilder {
	StringBuilder string;

	MyStringBuilder(StringBuilder string) {
		this.string = string;
	}

	public void setString(StringBuilder string) {
		this.string = string;
	}

	public StringBuilder getString() {
		return string;
	}

	public  void myAppend(String str) {
		int i = 0;
		while (i < 100) {
			i++;
			string.append(str + i);
		}

	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return string.toString();
	}
}

class ProducerThread extends Thread {
	private MyStringBuilder string = null;

	public ProducerThread(MyStringBuilder string) {
		this.string = string;
	}

	@Override
	public void run() {
		System.out.println("I am prod thread");
		string.myAppend(" prod ");
	}
}

class ConsumerThread extends Thread {

	private MyStringBuilder string = null;

	public ConsumerThread(MyStringBuilder string) {
		this.string = string;
	}

	@Override
	public void run() {
		System.out.println("I am con thread");
		string.myAppend(" cons ");
	}
}

6)

public class Thread1 {
	public static void main(String[] args) {

		MyStringBuilder string = new MyStringBuilder(new StringBuilder("shared "));

		Thread thread = new ProducerThread(string);
		Thread thread2 = new ConsumerThread(string);

		thread.start();
		thread2.start();

		try {
			thread.join();
			thread2.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		System.out.println(string);
	}
}

class MyStringBuilder {
	StringBuilder string;

	MyStringBuilder(StringBuilder string) {
		this.string = string;
	}

	public void setString(StringBuilder string) {
		this.string = string;
	}

	public StringBuilder getString() {
		return string;
	}

	public synchronized void myAppend(String str) {
		int i = 0;
		while (i < 100) {
			i++;
			string.append(str + i);
		}

	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return string.toString();
	}
}

class ProducerThread extends Thread {
	private MyStringBuilder string = null;

	public ProducerThread(MyStringBuilder string) {
		this.string = string;
	}

	@Override
	public void run() {
		System.out.println("I am prod thread");
		string.myAppend(" prod ");
	}
}

class ConsumerThread extends Thread {

	private MyStringBuilder string = null;

	public ConsumerThread(MyStringBuilder string) {
		this.string = string;
	}

	@Override
	public void run() {
		System.out.println("I am con thread");
		string.myAppend(" cons ");
	}
}

7) Synchronized with non-synchronized :

public class Thread1 {
	public static void main(String[] args) {

		MyStringBuilder string = new MyStringBuilder(new StringBuilder("shared "));

		Thread thread = new ProducerThread(string);
		thread.setName("Producer Thread");
		Thread thread2 = new ConsumerThread(string);
		thread2.setName("consumer Thread");

		thread.start();
		thread2.start();

		try {
			thread.join();
			thread2.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		System.out.println(string);
	}
}

class MyStringBuilder {
	StringBuilder string;

	MyStringBuilder(StringBuilder string) {
		this.string = string;
	}

	public void setString(StringBuilder string) {
		this.string = string;
	}

	public StringBuilder getString() {
		return string;
	}

	public synchronized void myAppend(String str) {
		int i = 0;
		while (i < 100) {
			i++;
			synchronized (string) {				
				string.append(str + i);
			}
		}

	}

	public void nonSychronizedMyAppend(String str) {
		int i = 100;
		while (i < 200) {
			i++;
			string.append(str + i);
		}

	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return string.toString();
	}
}

class ProducerThread extends Thread {
	private MyStringBuilder string = null;

	public ProducerThread(MyStringBuilder string) {
		this.string = string;
	}

	@Override
	public void run() {
		System.out.println("I am prod thread");
		string.myAppend(" prod ");
		string.nonSychronizedMyAppend(" prodNon ");
	}
}

class ConsumerThread extends Thread {

	private MyStringBuilder string = null;

	public ConsumerThread(MyStringBuilder string) {
		this.string = string;
	}

	@Override
	public void run() {
		System.out.println("I am con thread");
		string.nonSychronizedMyAppend(" consNon ");
		string.myAppend(" cons ");
	}
}

8) Non-synchronized in Synchronized :

public class Thread1 {
	public static void main(String[] args) {

		MyStringBuilder string = new MyStringBuilder(new StringBuilder("shared "));

		Thread thread = new ProducerThread(string);
		thread.setName("Producer Thread");
		Thread thread2 = new ConsumerThread(string);
		thread2.setName("consumer Thread");

		thread.start();
		thread2.start();

		try {
			thread.join();
			thread2.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		System.out.println(string);
	}
}

class MyStringBuilder {
	StringBuilder string;

	MyStringBuilder(StringBuilder string) {
		this.string = string;
	}

	public void setString(StringBuilder string) {
		this.string = string;
	}

	public StringBuilder getString() {
		return string;
	}

	public synchronized void myAppend(String str) {
		int i = 0;
		while (i < 100) {
			i++;
			string.append(str + i);
		}
		nonSychronizedMyAppend(str + "non");
	}

	public void nonSychronizedMyAppend(String str) {
		int i = 100;
		while (i < 200) {
			i++;
			string.append(str + i);
		}

	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return string.toString();
	}
}

class ProducerThread extends Thread {
	private MyStringBuilder string = null;

	public ProducerThread(MyStringBuilder string) {
		this.string = string;
	}

	@Override
	public void run() {
		System.out.println("I am prod thread");
		string.myAppend(" prod ");
	}
}

class ConsumerThread extends Thread {

	private MyStringBuilder string = null;

	public ConsumerThread(MyStringBuilder string) {
		this.string = string;
	}

	@Override
	public void run() {
		System.out.println("I am con thread");
		string.myAppend(" cons ");
	}
}

9) deadlock?

public class Thread1 {
	public static void main(String[] args) {

		MyStringBuilder string = new MyStringBuilder(new StringBuilder("shared "));

		Thread thread = new ProducerThread(string);
		thread.setName("Producer Thread");
		Thread thread2 = new ConsumerThread(string);
		thread2.setName("consumer Thread");
		
		thread.start();
		thread2.start();

		try {
			thread.join();
			thread2.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		System.out.println(string);
	}
}

class MyStringBuilder {
	StringBuilder string;

	MyStringBuilder(StringBuilder string) {
		this.string = string;
	}

	public void setString(StringBuilder string) {
		this.string = string;
	}

	public StringBuilder getString() {
		return string;
	}

	public synchronized void myAppend(String str) {
		int i = 0;
		while (i < 100) {
			i++;
			string.append(str + i);
		}

	}
	
	public synchronized void myAppend2(String str) {
		int i = 100;
		while (i < 200) {
			i++;
			string.append(str + i);
		}

	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return string.toString();
	}
}

class ProducerThread extends Thread {
	private MyStringBuilder string = null;

	public ProducerThread(MyStringBuilder string) {
		this.string = string;
	}

	@Override
	public void run() {
		System.out.println("I am prod thread");
		string.myAppend(" prod ");
		string.myAppend2(" prodNon ");
	}
}

class ConsumerThread extends Thread {

	private MyStringBuilder string = null;

	public ConsumerThread(MyStringBuilder string) {
		this.string = string;
	}

	@Override
	public void run() {
		System.out.println("I am con thread");
		string.myAppend2(" consNon ");
		string.myAppend(" cons ");
	}
}

10) deadlock ?

public class Thread1 {
	public static void main(String[] args) {

		MyStringBuilder string1 = new MyStringBuilder(new StringBuilder("resource_one "));
		MyStringBuilder string2 = new MyStringBuilder(new StringBuilder("resource_two "));

		Thread thread = new ProducerThread(string1, string2);
		thread.setName("Producer Thread");
		Thread thread2 = new ConsumerThread(string2, string1);
		thread2.setName("consumer Thread");

		thread.start();
		thread2.start();

		try {
			thread.join();
			thread2.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		System.out.println(string1);
		System.out.println(string2);
	}
}

class MyStringBuilder {
	StringBuilder string;

	MyStringBuilder(StringBuilder string) {
		this.string = string;
	}

	public void setString(StringBuilder string) {
		this.string = string;
	}

	public StringBuilder getString() {
		return string;
	}

	public synchronized void myAppend(String str) {
		int i = 0;
		while (i < 100) {
			i++;
			string.append(str + i);
		}

	}

	public synchronized void myAppend2(String str) {
		int i = 100;
		while (i < 200) {
			i++;
			string.append(str + i);
		}

	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return string.toString();
	}
}

class ProducerThread extends Thread {
	private MyStringBuilder string1 = null;
	private MyStringBuilder string2 = null;

	public ProducerThread(MyStringBuilder string1, MyStringBuilder string2) {
		this.string1 = string1;
		this.string2 = string2;
	}

	@Override
	public void run() {
		System.out.println("I am prod thread");
		string2.myAppend2(" prod ");
		string1.myAppend(" prodNon ");
	}
}

class ConsumerThread extends Thread {

	private MyStringBuilder string1 = null;
	private MyStringBuilder string2 = null;

	public ConsumerThread(MyStringBuilder string1, MyStringBuilder string2) {
		this.string1 = string1;
		this.string2 = string2;
	}

	@Override
	public void run() {
		System.out.println("I am con thread");
		string2.myAppend(" consNon ");
		string1.myAppend2(" cons ");
	}
}

Last updated