Java线程(二):线程数据共享及ThreadLocal使用

一、线程范围内共享数据

1、线程范围内共享数据是指要实现这三个对象在同一个线程上访问的数据的一致性,不同线程访问的差异性。

2、如有三个对象A、B、C,两个线程@,#,那么线程范围内共享数据就是指ABC在线程@上访问的数据是相同的。而三个对象访问 @和访问 # 的数据时不同的。 有几个线程就会有几份数据。如下图所示:

3、实现:利用Map<Thraed,data>来实现,Map的Key是关键。

4、代码实现:

1)线程会访问模块;

2)线程负责放数据;

3)模块负责从访问的线程中拿数据。

4)验证单个线程在访问不同模块时是否共享数据??

package com.Thread;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 *
 * ThreadScopeShareData.java
 *
 * @title 线程范围内共享变量
 * @description 各模块(A/B)在某个线程上的数据是一致的:线程范围内的数据
 * @author SAM-SHO
 * @Date 2014-8-17
 */
public class ThreadScopeShareData {

	//传递数据的容器,这个是关键
	// map的key为线程
	private static Map<Thread,Integer> threadData = new HashMap<Thread, Integer>();

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		ThreadScopeShareData tShareData = new ThreadScopeShareData();
		tShareData.init();
	}

	/**
	 * 定义初始化方法,
	 * 解决main(静态)方法不能创建内部类实例对象
	 */
	public void init() {
		FirstThread firstThread = new FirstThread();
		Thread fThread = new Thread(firstThread);
		fThread.start();

		SecondThread secondThread = new SecondThread();
		secondThread.start();
	}

	//模块A
	static class A {
		public void get() {
			int data = threadData.get(Thread.currentThread());//从线程获取数据
			System.out.println("A from " + Thread.currentThread().getName()
					+ " get data :" + data);
		}
	}

	// 模块B
	static class B {
		public void get() {
			int data = threadData.get(Thread.currentThread());//从线程获取数据
			System.out.println("B from " + Thread.currentThread().getName()
					+ " get data :" + data);
		}
	}

	/*
	 * 线程一
	 */
	class FirstThread implements Runnable {

		@Override
		public void run() {
			int data = new Random().nextInt();
			threadData.put(Thread.currentThread(), data);//把数据放入MAP

			System.out.println(Thread.currentThread().getName()
								+ " has put data :" + data);
			new A().get();
			new B().get();
		}
	}

	/*
	 * 线程二
	 */
	class SecondThread extends Thread {

		@Override
		public void run() {
			int data = new Random().nextInt();
			threadData.put(Thread.currentThread(), data);//放入MAP

			System.out.println(Thread.currentThread().getName()
					+ " has put data :" + data);
			new A().get();
			new B().get();
		}
	}

}

【输出】

Thread-0 has put data :372405571
Thread-1 has put data :-1925902112
A from Thread-0 get data :372405571
A from Thread-1 get data :-1925902112
B from Thread-0 get data :372405571
B from Thread-1 get data :-1925902112

5、分析:

1)线程0放入 ‘372405571’ ,线程1放入 ‘ -1925902112’

2)A模块从线程0 取出 372405571, B模块从线程0 取出 372405571。即共享了数据。

3)A模块从线程1 取出 -1925902112, B模块从线程1 取出 -1925902112。即共享了数据。

二、ThreadLocal的使用

1、ThreadLocal 可以轻松实现线程范围内的数据共享,即使用ThreadLocal<data> 取代上面的Map<Thread,data>。

2、使用:每个线程使用调用ThreadLocal 的set()方法放入共享数据,而每个模块只需要调用get()获取即可,非常简单。

修改上面代码如下:

package com.practise.tradition;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import com.Thread.ThreadScopeShareData;

/**
 *
 * ThreadScopeData.java
 *
 * @title 线程范围内的共享数据
 * @description 各模块(A/B)在某个线程上的数据是一致的:线程范围内的数据
 * @author SAM-SHO
 * @Date 2014-11-8
 */
public class ThreadScopeData {

	// 传递数据的容器,这个是关键
	// map的key为线程
	// private static Map<Thread,Integer> threadData = new HashMap<Thread,Integer>();

	//用 ThreadLocal 取代 Map<Thread,data>。
	private static ThreadLocal<Integer> xThreadLocal = new ThreadLocal<Integer>();

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		ThreadScopeShareData tShareData = new ThreadScopeShareData();
		tShareData.init();
	}

	/**
	 * 定义初始化方法, 解决main(静态)方法不能创建内部类实例对象
	 */
	public void init() {
		FirstThread firstThread = new FirstThread();
		Thread fThread = new Thread(firstThread);
		fThread.start();

		SecondThread secondThread = new SecondThread();
		secondThread.start();
	}

	// 模块A
	static class A {
		public void get() {
			// int data = threadData.get(Thread.currentThread());//从线程获取数据

			int data = xThreadLocal.get();// 直接获取即可,ThreadLocal自动会区分线程
			System.out.println("A from " + Thread.currentThread().getName() + " get data :" + data);
		}
	}

	// 模块B
	static class B {
		public void get() {
			// int data = threadData.get(Thread.currentThread());//从线程获取数据

			int data = xThreadLocal.get();// 直接获取即可,ThreadLocal自动会区分线程
			System.out.println("B from " + Thread.currentThread().getName() + " get data :" + data);
		}
	}

	/*
	 * 线程一
	 */
	class FirstThread implements Runnable {

		@Override
		public void run() {
			int data = new Random().nextInt();
			// threadData.put(Thread.currentThread(), data);//把数据放入MAP

			xThreadLocal.set(data);// 直接放数据,取代那个map

			System.out.println(Thread.currentThread().getName() + " has put data :" + data);
			new A().get();
			new B().get();
		}
	}

	/*
	 * 线程二
	 */
	class SecondThread extends Thread {

		@Override
		public void run() {
			int data = new Random().nextInt();
			// threadData.put(Thread.currentThread(), data);//放入MAP

			xThreadLocal.set(data);// 直接放数据,取代那个map

			System.out.println(Thread.currentThread().getName() + " has put data :" + data);
			new A().get();
			new B().get();
		}
	}

}

3、共享复杂数据,如是一个数据类。

可以使用单例,并且利用ThreadLocal 改造单例,实现线程安全。

package com.Thread;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 *
 * ThreadLocalTest.java
 *
 * @title ThreadLocal失效线程内数据共享
 * @description
 * @author SAM-SHO
 * @Date 2014-8-23
 */
public class ThreadLocalTest {

	private static int data = 0;

	private static Map<Thread, Integer> threadData = new HashMap<Thread, Integer>();// 传递数据

	private static ThreadLocal<Integer> xThreadLocal = new ThreadLocal<Integer>();

	private static ThreadLocal<MyData> myDataThreadLocal = new ThreadLocal<MyData>();

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		ThreadLocalTest tThreadLocalTest = new ThreadLocalTest();
		tThreadLocalTest.init();
	}

	/**
	 * 定义初始化方法, 解决main(静态)方法不能创建内部类实例对象
	 */
	public void init() {
		FirstThread firstThread = new FirstThread();
		Thread fThread = new Thread(firstThread);
		fThread.start();

		SecondThread secondThread = new SecondThread();
		Thread sThread = new Thread(secondThread);
		sThread.start();
	}

	static class A {
		public void get() {
			// int data = threadData.get(Thread.currentThread());

			int data = xThreadLocal.get();// 利用ThreadLocal直接拿数据

			// MyData myData = myDataThreadLocal.get();
			// System.out.println(myData.toString());

			MyNewData myNewData = MyNewData.getThreadInstance();

			System.out.println("A from " + Thread.currentThread().getName() + " get data :" + data + "," + myNewData.getName() + "," + myNewData.getAge());
		}
	}

	static class B {
		public void get() {
			// int data = threadData.get(Thread.currentThread());

			int data = xThreadLocal.get();// 利用ThreadLocal直接拿数据

			// MyData myData = myDataThreadLocal.get();
			// System.out.println(myData.toString());

			MyNewData myNewData = MyNewData.getThreadInstance();

			System.out.println("B from " + Thread.currentThread().getName() + " get data :" + data + "," + myNewData.getName() + "," + myNewData.getAge());
		}
	}

	/*
	 * 线程一
	 */
	class FirstThread implements Runnable {

		@Override
		public void run() {
			int data = new Random().nextInt();
			// threadData.put(Thread.currentThread(), data);//放入MAP

			xThreadLocal.set(data);// 利用ThreadLocal存放数据

			System.out.println(Thread.currentThread().getName() + " has put data :" + data);

			// 简单对象
			// MyData myData = new MyData("shaoxiaobao",3);
			// myDataThreadLocal.set(myData);

			// 单例对象
			MyNewData myNewData = MyNewData.getThreadInstance();
			myNewData.setAge(data);
			myNewData.setName("name" + data);

			new A().get();
			new B().get();
		}
	}

	/*
	 * 线程二
	 */
	class SecondThread extends Thread {
		// private int data;
		// public SecondThread(int data) {
		// this.data = data;
		// }

		@Override
		public void run() {
			int data = new Random().nextInt();
			// threadData.put(Thread.currentThread(), data);//放入MAP

			xThreadLocal.set(data);// 利用ThreadLocal存放数据

			System.out.println(Thread.currentThread().getName() + " has put data :" + data);
			// 简单对象
			// MyData myData = new MyData("zhaoxioaniu",2);
			// myDataThreadLocal.set(myData);

			// 单例对象
			MyNewData myNewData = MyNewData.getThreadInstance();
			myNewData.setAge(data);
			myNewData.setName("name" + data);

			new A().get();
			new B().get();
		}
	}

	/**
	 * @title 简单写法
	 * @description
	 * @author SAM-SHO
	 * @Date 2014-8-23
	 */
	class MyData {

		private String name;
		private int age;

		public MyData() {
		};

		public MyData(String name, int age) {
			super();
			this.name = name;
			this.age = age;
		}

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

		public int getAge() {
			return age;
		}

		public void setAge(int age) {
			this.age = age;
		}

		@Override
		public String toString() {
			return "MyData [age=" + age + ", name=" + name + "]";
		}

	}

}

/**
 * @title 单利数据 高级实现 交给数据类的对象
 * @description
 * @author SAM-SHO
 * @Date 2014-8-23
 */
class MyNewData {
	private MyNewData() {
	};// 私有构造方法

	/*
	 * private static MyNewData instance = null;
	 *
	 * //懒汉单例:防止内存中存在两个单例的对象,要互斥,效率太低 public static synchronized MyNewData
	 * getInstance() {
	 *
	 * if(instance==null) { instance = new MyNewData(); } return instance; }
	 */

	// 改造单例,可以不加互斥
	private static ThreadLocal<MyNewData> map = new ThreadLocal<MyNewData>();// 定义ThreadLocal

	public static MyNewData getThreadInstance() {
		MyNewData instance = map.get();// ThreadLocal先拿,没有就创建
		if (instance == null) {
			instance = new MyNewData();
			map.set(instance);// 创建完,就放入ThreadLocal
		}
		return instance;
	}

	private String name;
	private int age;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "MyData [age=" + age + ", name=" + name + "]";
	}

}

【输出】

Thread-0 has put data :1074769954
Thread-2 has put data :-617831879
A from Thread-0 get data :1074769954,name1074769954,1074769954
A from Thread-2 get data :-617831879,name-617831879,-617831879
B from Thread-0 get data :1074769954,name1074769954,1074769954
B from Thread-2 get data :-617831879,name-617831879,-617831879

三、多个线程共享数据

问题:四个线程,其中2个线程每次对J增加1,另外2个线程对J减少1。

1、如果每个线程执行的代码相同,可以使用同一 Runnable 对象,这个 Runnable 对象中有那个共享数据,例如卖票系统就可以这么做。

2、如果每个线程执行的代码不同,这时候需要不同的 Runnable 对象,有如下两种方式来实现这些 Runnable 对象之间的数据共享。

1)将共享数据(J)封装在另外一个对象(ShareData)中,然后将这个对象逐一传递给各个 Runnable 对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成。这样容易实现针对该数据进行的各个操作的互斥与通信。如:

package com.Thread;

/**
 *
 * MultiThreadShareData.java
 *
 * @title 多个线程共享数据
 * @description
 * 四个线程,其中2个线程每次对J增加1,另外2个线程对J减少1.
 * @author SAM-SHO
 * @Date 2014-8-23
 */
public class MultiThreadShareData {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		MultiThreadShareData ttt = new MultiThreadShareData();
		ttt.init();
	}

	/**
	 * 1、如果每个线程执行的代码相同,可以使用同一 Runnable 对象,
	 * 这个 Runnable 对象中有那个共享数据,例如卖票系统就可以这么做
	 * 2、如果每个线程执行的代码不同,这时候需要不同的 Runnable 对象,
	 * 有如下两种方式来实现这些 Runnable 对象之间的数据共享。
	 * 1)将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个 Runnable 对象。
	 * 每个线程对共享数据的操作方法也分配到哪个对象身上去完成。这样容易实现针对该数据
	 * 进行的各个操作的互斥与通信()
	 * 2)将这些 Runnable 对象作为某一类中的内部类,共享数据作为这个外部类中的成员变量
	 * ,每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥与通信
	 * ,作为内部类的各个 Runnable 对象调用外部类的这个方法。
	 *
	 * 3)上面两种方式的结合:将共享数据封装在另外一个对象中,每个线程对共享数据的操作方法
	 * 也分配到那个对象身上去完成。对象作为这个外部类中的成员变量或方法中的局部变量,每个线程
	 * 的 Runnable 对象作为外部类中的成员内部类或局部内部类
	 *
	 * 总之,要同步互斥的极几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中
	 * ,这样比较容易实现它们之间的同步互斥与通信。
	 */
	private void init(){
		ShareData tShareData = new ShareData();//同一个对象

		OneThread oneThread= new OneThread(tShareData);
		Thread thread1 = new Thread(oneThread);
		thread1.start();

		TwoThread twoThread= new TwoThread(tShareData);
		Thread thread2 = new Thread(twoThread);
		thread2.start();

		ThirdThread thirdThread= new ThirdThread(tShareData);
		Thread thread3 = new Thread(thirdThread);
		thread3.start();

		FourThread fourThread= new FourThread(tShareData);
		Thread thread4 = new Thread(fourThread);
		thread4.start();

		final ShareData mShareData = new ShareData();

	}

	class OneThread implements Runnable{
		private ShareData tShareData;

		public OneThread() {}

		public OneThread(ShareData tShareData) {
			this.tShareData = tShareData;
		}
		@Override
		public void run() {
			while(true) {
				tShareData.add();
			}
		}

	}
	class TwoThread implements Runnable{
		private ShareData tShareData;

		public TwoThread(ShareData tShareData) {
			this.tShareData = tShareData;
		}
		@Override
		public void run() {
			while(true) {
				tShareData.add();
			}
		}

	}
	class ThirdThread implements Runnable{

		private ShareData tShareData;

		public ThirdThread(ShareData tShareData) {
			this.tShareData = tShareData;
		}

		@Override
		public void run() {
			while(true) {
				tShareData.minus();
			}
		}

	}
	class FourThread implements Runnable{

		private ShareData tShareData;

		public FourThread(ShareData tShareData) {
			this.tShareData = tShareData;
		}
		@Override
		public void run() {
			while(true) {
				tShareData.minus();
			}
		}
	}

	/**
	 * 业务资源类
	 * MultiThreadShareData.java
	 *
	 * @title
	 * @description
	 * @author SAM-SHO
	 * @Date 2014-11-11
	 */
	class ShareData {
		private int j = 0;

		//加法
		public synchronized void add() {
			j++;
			System.out.println(Thread.currentThread().getName()+ " 加法   ---->"+j);
		}

		//加法
		public synchronized void minus() {
			j--;
			System.out.println(Thread.currentThread().getName()+ " 减法   <----"+j);
		}

	}

}

2)将这些 Runnable 对象作为某一类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥与通信,作为内部类的各个 Runnable 对象调用外部类的这个方法。如

package com.Thread;

/**
 *
 * MultiThreadShareData.java
 *
 * @title 多个线程共享数据
 * @description
 * 四个线程,其中2个线程每次对J增加1,另外2个线程对J减少1.
 * @author SAM-SHO
 * @Date 2014-8-23
 */
public class MultiThreadShareData2 {

	//共享数据作为外部类的成员变量
	private int j = 0 ;

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		MultiThreadShareData2 tt = new MultiThreadShareData2();

		OneThread tOneThread = tt. new OneThread();
		TwoThread tTwoThread = tt. new TwoThread();

		//起两个线程
		for (int i = 0; i < 2; i++) {
			Thread t = new Thread(tOneThread);
			t.start();
			t = new Thread(tTwoThread);
			t.start();
		}
	}

	// 对共享数据的操作方法也分配给外部类
	// 方便实现互斥与通信
	private  synchronized void add() {
		j++;
		System.out.println(Thread.currentThread().getName()+ " 加法   ---->"+j);

	}

	// 对共享数据的操作方法也分配给外部类
	// 方便实现互斥与通信
	private synchronized void minus() {
		j--;
		System.out.println(Thread.currentThread().getName()+ " 减法   <----"+j);

	}

	// 内部类 Runnable 1
	class OneThread implements Runnable{

		@Override
		public void run() {
			for (int i = 0; i < 10; i++) {
				add();
			}
		}

	}

	// 内部类 Runnable 2
	class TwoThread implements Runnable{
		@Override
		public void run() {
			for (int i = 0; i < 10; i++) {
				minus();
			}
		}

	}	

	/**
	 * 业务资源类
	 * MultiThreadShareData.java
	 *
	 * @title
	 * @description
	 * @author SAM-SHO
	 * @Date 2014-11-11
	 */
	class ShareData {
		private int j = 0;

		//加法
		public synchronized void add() {
			j++;
			System.out.println(Thread.currentThread().getName()+ " 加法   ---->"+j);
		}

		//加法
		public synchronized void minus() {
			j--;
			System.out.println(Thread.currentThread().getName()+ " 减法   <----"+j);
		}
	}
}

3)上面两种方式的结合:将共享数据封装在另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象身上去完成。对象作为这个外部类中的成员变量或方法中的局部变量,每个线程的 Runnable 对象作为外部类中的成员内部类或局部内部类

4)总之,要同步互斥的极几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥与通信。

时间: 2024-08-09 15:27:18

Java线程(二):线程数据共享及ThreadLocal使用的相关文章

Android(线程二) 线程池详解

我们在ListView中需要下载资源时,赞不考虑缓存机制,那么每一个Item可能都需要开启一个线程去下载资源(如果没有线程池),如果Item很多,那么我们可能就会无限制的一直创建新的线程去执行下载任务,最终结果可能导致,应用卡顿.手机反应迟钝!最坏的结果是,用户直接卸载掉该App.所以,我们在实际开发中需要考虑多线程,多线程就离不开线程池.如果你对线程还不了解,可以看看这篇文章,Android(线程一) 线程. 使用线程池的优点: (1).重用线程,避免线程的创建和销毁带来的性能开销: (2).

C#中的线程(二) 线程同步基础

1.同步要领 下面的表格列展了.NET对协调或同步线程动作的可用的工具:                       简易阻止方法 构成 目的 Sleep 阻止给定的时间周期 Join 等待另一个线程完成                       锁系统 构成 目的 跨进程? 速度 lock 确保只有一个线程访问某个资源或某段代码. 否 快 Mutex 确保只有一个线程访问某个资源或某段代码.可被用于防止一个程序的多个实例同时运行. 是 中等 Semaphore 确保不超过指定数目的线程访问某

线程二-----------线程池的问题

借鉴了几位大神的博客,学习下 线程池的作用: 线程池作用就是限制系统中执行线程的数量. 根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果:少了浪费了系统资源,多了造成系统拥挤效率不高.用线程池控制线程数量,其他线程排队等候.一个任务执行完毕,再从队列的中取最前面的任务开始执行.若队列中没有等待进程,线程池的这一资源处于等待.当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了:否则进入等待队列. public class Test { /** * newSin

java线程(二) - 线程安全性

前言: 要编写线程安全的代码,其核心在于要对状态访问的操作进行管理,特别是对共享的和可变的状态的访问. 当多个线程访问同一个可变的状态变量时没有使用合适的同步,那么程序就会出现错误,有三种方式可以修复这个问题: 不在线程之间共享该状态变量 将状态变量修改为不可变的变量 在访问状态变量时使用同步 线程安全性的定义: 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要额外的同步或者协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的. 那

Java多线程 二 线程间通信

线程间通信: 多个线程在处理同一资源,但是 等待唤醒机制 涉及的方法: 1.wait() 让线程处于冻结状态,被wait的线程会被存储到线程池中. 2.notify() 唤醒线程池中的一个线程(任意) 3.notifyAll() 唤醒线程池中的所有线程.. 这些方法都必须定义在同步中, 因为这些方法是用于操作线程状态的方法. 必须明确到底操作的那个锁上的线程. 为什么操作线程的方法wait notify notifyAll定义在了Object中. 因为这些方法是监视器方法,监视器其实就是锁. 锁

java线程:单例隐藏ThreadLocal实现线程数据共享

问题: 给定的二叉查找树中,有两个节点不小心被调换了位置,现在需要将其修正,不改变树的结构. 分析: 二叉排序树的中序遍历是有序的,所以这个问题又是建立在中序遍历模板上的问题,所以我们可以对其进行中序遍历,并用一个pre指针指向当前遍历结果中的最后一个结点,即下次遍历前的前一个结点.然后就可以通过将当前结点与pre结点进行比较,来判断是否有序了.若乱序,就将这两个结点都放入到预先定义的容器中. 错误的形式就这两种,我们看到,在乱序容器中,最多就存了四个元素,所以空间复杂度还是满足O(n)的,当然

Java并发机制(4)--ThreadLocal线程本地变量(转)

转自:博客园-海子-http://www.cnblogs.com/dolphin0520/p/3920407.html Java并发编程:深入剖析ThreadLocal 首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的.各个线程中访问的是不同的对象. 另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal

Java基础系列之(二) - 线程

一.线程的实现方式 1.继承Thread 2.实现Runnable接口 二.线程的状态 1.New(新生线程) 当你new一个Thread,newThread(r),这时处于线程的新生状态,此时程序还没有真正的运行. 2.Runnable(可运行的) 当启动start()方法时,此时线程处于可运行状态,不一定运行之中,这取决与线程是否得到CPU的运行时间片.事实上,一个线程并不是一直处于运行状态,偶尔需要被中断,让其他线程有运行的机会. 3.Blocked(被阻塞) 当发生以下情况被阻塞 -线程

Java线程与并发库高级应用-线程范围内共享数据ThreadLocal类

1.线程范围内共享变量 1.1 前奏: 使用一个Map来实现线程范围内共享变量 public class ThreadScopeShareData { static Map<Thread, Integer> dataMap = new HashMap<Thread, Integer>(); public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(new Runnable