`
xiaozhouzhou
  • 浏览: 13326 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

线程的同步通信与线程范围内的数据共享问题

 
阅读更多
线程的同步通信与线程范围内的数据共享问题
一、线程的同步通信
什么是线程的同步通信?他有什么作用?这是我们在看到一个新概念时多会想起的两个问题。线程的同步通信就是多个线程在做相关的事或者说在操作同一份数据时,线程之间建立一种互知的通信关系;它的作用是使线程之间能和睦共处,既其中某一线程做完这件事后,再通知另外的线程来做这件事,不出现混乱。如上一篇文章留下的面试问题就是一个很好的线程同步编程案例。下面给出详细的代码,和疑问解释。
public class CommunicationThread {
	final static Lock lock=new Lock();//实例化内部类
	public static void main(String args[]){
		new Thread(new Runnable() {
			public void run() {
				for(int i=1;i<=50;i++){
					lock.son(i);//调用子线程
				}
			}
		}).start();
		for(int i=1;i<=50;i++){
			lock.father(i);//调用父线程
		}
	}
	
	/*
	 * Lock类:将相关的互斥操作放在同一个类中
	 */
	static class Lock{
		boolean flag=true;//两个互斥线程的通信钥匙
		public synchronized void son(int i){
			while(!flag){//判断是否是该线程运行,此处也可用if语句,效果相同					 //但while语句可避免伪唤醒的情况发生,程序更安全。
				try {
					this.wait();//线程等待
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			for(int j=1;j<=10;j++){
				System.out.println("son:"+j+"of current"+i);
			}
			flag=false;
			this.notify();//唤醒等待中的线程
		}
		
		public synchronized void father(int i){
			while(flag){
				try {
					this.wait();//线程等待
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			for(int j=1;j<=100;j++){
				System.out.println("father:"+j+"of current"+i);
			}
			flag=true;
			this.notify();//唤醒等待中的线程
		}
	}
}

疑问:问什么要给已经互斥了的线程加上信息判断?这是为了防止电脑主机CPU介入线程的运行,当子线程运行结束之后本因该父线程运行,这时主机CPU就可能介入,再次让子线程来运行,这样就无法达到预期效果。所以在每个线程运行前都判断一下是否该自己运行了,不是的话就就“等待”,并且“唤醒”另外在等待中的线程。所以线程间的通信技术很有用。
----------------------------分割线-----------------------------------

二、线程范围内的数据数据问题:
这个技术点在java ee和底层框架中运用很多,所以很重要,很有必要去弄懂、弄清楚其中的原理。
下面就讲些我的理解:


上图线程1在运行时,调用A、B、C三个模块或对象,这三个模块处理的又是线程1绑定的同一个数据;线程2在运行时,也调用A、B、C三个模块或对象,这三个模块处理的又是线程2绑定的同一个数据,不再是先前线程1的数据了.这种原理就是线程范围内的数据共享,线程间的数据是不一样的,而线程内部运行的数据却一直是同一份。加入有1000个人同时访问一台服务器,这就得开设1000个线程,如果不运用上述原理,就会很混乱,数据调用出问题。所以,线程范围内的数据共享保证了别的线程无法干涉自己线程内部的数据,即使出了问题,也只是单方面的。
下面给出代码加以参考:
public class ShareData {
	private static int data=0;//静态全局变量
	public static void main(String[] args) {
		for(int i=0;i<2;i++){//创建两个线程
			new Thread(new Runnable(){
				public void run() {
					data=new Random().nextInt();
					System.out.println(Thread.currentThread().getName()
							+" has put data:"+data);
					new A().get();//模块A取得数据
					new B().get();//模块B取得数据
				}
				
			}).start();//线程开启
		}

	}
	//静态模块A
	static class A{
		public void get(){
			System.out.println("A from"+Thread.currentThread().getName()
					+" get data:"+data);
		}
	}
	//静态模块B
	static class B{
		public void get(){
			System.out.println("B from"+Thread.currentThread().getName()
					+"get data:"+data);
		}
	}
}

运行结果:



上面的结果明显不适我们想要的。我们需要的是:模块A、B从第一个线程中取得数据相同而与模块A、B从第二个线程取得的数据不同。为什么得不到我们想要的结果呢?这里就是线程内部的数据共享出了问题,线程1调用的模块A、B取得了线程2的数据。那这个问题该怎么解决呢?
只要对上面的代码稍加处理就可以解决了:
public class ShareData {
	//建立哈希表,存取数据
	private static Map<Thread,Integer> threadData=new HashMap<Thread,Integer>();
	public static void main(String[] args) {
		for(int i=0;i<2;i++){//创建两个线程
			new Thread(new Runnable(){
				public void run() {
					int	data=new Random().nextInt();//取得数据
					System.out.println(Thread.currentThread().getName()
							+" has put data:"+data);
					threadData.put(Thread.currentThread(), data);
					new A().get();//模块A取得数据
					new B().get();//模块B取得数据
				}
				
			}).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{
		int data = threadData.get(Thread.currentThread());
		public void get(){
			System.out.println("B from"+Thread.currentThread().getName()
					+"get data:"+data);
		}
	}
}

运行结果:


这时得到的结果就是预期想要的了,各线程间的数据互不影响,线程内部的数据能够共享。解决上述问题除了用哈希表外,还可以用ThreadLocal类,这个类的使用更加方便有效,有兴趣的人,可以尝试使用,下面就不给出参考代码了。
----------------------------------------分割线------------------------
线程同步问题现在告以段落了,以上2篇文章都是我在学习中遇到的疑问及理解,如有不足之处还请包含,同时也希望能给你带来些许帮助。
  • 大小: 33.8 KB
  • 大小: 15.4 KB
  • 大小: 15.6 KB
1
3
分享到:
评论

相关推荐

    C#线程锁介绍源码

    互斥体Mutex和事件对象EventWaitHandler属于内核对象,利用内核对象进行线程同步,线程必须要在用户模式和内核模 式间切换,所以一般效率很低,但利用互斥对象和事件对象这样的内核对象,可以在多个进程中的各个...

    并行计算导论(原书第2版).[美]Ananth Grama(带详细书签).pdf

    原版自1993年出版第1版到2003年出版第2版以来,已在世界范围内被广泛地采用为高等院校本科生和研究生的教材或参考书。 第1章 并行计算介绍 1.1 推动并行化 1.1.1 计算能力因素——从晶体管到浮点运算速度 1.1.2 ...

    Java性能优化

    控制数据共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信。 2.尽量避免随意使用静态变量 要知道,当某个对象被定义为stataic的变量所引用,那么GC通常是不会回收这个对象所占有的内存,如 ...

    成熟量产扫地机代码 STM32 FreeRTos功能完整 代码注释清晰IIC、PWM、SPI、多路ADC与DMA、IAP

    2.任务同步和互斥:STM32FreeRTos提供了任务同步和互斥的功能,可以实现多个任务之间的协作和共享数据。3.定时器和时钟管理:STM32FreeRTos提供了定时器和时钟管理的功能,能够按照预设的时间周期定时触发相应的任务...

    超级有影响力霸气的Java面试题大全文档

    例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。 当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望...

    java 面试题 总结

    例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。 当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望...

    Visual C++程序开发范例宝典(光盘) 第四部分

    实例239 查询日期控件内数据 实例240 控件作为字段、操作符和内容进行查询 实例241 巧妙获取年龄 实例242 格式化金额 实例243 如何随机显示记录 9.3 查询前若干名数据 实例244 查询前10名数据 实例245 取出...

    Visual C++程序开发范例宝典(光盘) 第八部分

    实例239 查询日期控件内数据 实例240 控件作为字段、操作符和内容进行查询 实例241 巧妙获取年龄 实例242 格式化金额 实例243 如何随机显示记录 9.3 查询前若干名数据 实例244 查询前10名数据 实例245 取出...

    UNIX 高级教程系统技术内幕

    7.4 多处理器同步问题 7.4.1 唤醒丢失问题 7.4.2 巨群问题 7.5 信号灯 7.5.1 提供互斥访问的信号灯 7.5.2 使用的信号灯的事件等待 7.5.3 用于控制可计数资源的信号灯 7.5.4 信号灯的缺点 7.5.5 护卫 7.6 自旋锁 ...

    Visual C++ 程序开发范例宝典 源码 光盘 part2

    cc实例184 执行一个外部程序直到其结束 cc实例185 调用具有参数的可执行程序 6.7 线程同步 cc实例186 利用事件对象实现线程同步 cc实例187 利用互斥对象实现线程同步 cc实例188 利用临界区实现线程同步 ...

    JAVA面试题最全集

    多线程,用什么关键字修饰同步方法?stop()和suspend()方法为何不推荐使用? 59.使用socket建立客户端与服务器的通信的过程 60.JAVA语言国际化应用,Locale类,Unicode 61.描述反射机制的作用 62.如何读写一个...

    Visual C++程序开发范例宝典(PDF扫描版).part3

     cc实例239 查询日期控件内数据   cc实例240 控件作为字段.c操作符和内容进行查询   cc实例241 巧妙获取年龄   cc实例242 格式化金额   cc实例243 如何随机显示记录   9.3 查询前若干名数据   cc...

    Visual C++程序开发范例宝典(PDF扫描版).part2

     cc实例239 查询日期控件内数据   cc实例240 控件作为字段.c操作符和内容进行查询   cc实例241 巧妙获取年龄   cc实例242 格式化金额   cc实例243 如何随机显示记录   9.3 查询前若干名数据   cc...

    Delphi5开发人员指南

    11.3.2 线程同步 317 11.4 一个多线程的示范程序 325 11.4.1 用户界面 326 11.4.2 搜索线程 330 11.4.3 调整优先级 334 11.5 多线程与数据库 335 11.6 多线程与图形处理 340 11.7 总结 343 第12章 文件处理 344 12.1...

    API之网络函数---整理网络函数及功能

    其中包括文件、文件映射、进程、线程、安全和同步对象等 CompareFileTime 对比两个文件的时间 CopyFile 复制文件 CreateDirectory 创建一个新目录 CreateFile 打开和创建文件、管道、邮槽、通信服务、设备以及...

    软件工程-理论与实践(许家珆)习题答案

    获取足够多的问题领域的知识,需求抽取的方法一般有问卷法、面谈法、数据采集法、用例法、情景实例法以及基于目标的方法等;还有知识工程方法,例如,场记分析法、卡片分类法、分类表格技术和基于模型的知识获取等 ...

    C#微软培训资料

    14.4 继承中关于属性的一些问题.169 14.5 小 结 .172 第四部分 深入了解 C#.174 第十五章 接 口 .174 15.1 组件编程技术 .174 15.2 接 口 定 义 .177 15.3 接口的成员 .178 15.4 接口的实现 .182 ...

Global site tag (gtag.js) - Google Analytics