`

多个线程之间共享数据的方式

阅读更多

ps:由于最近的面试很多问题都涉及多线程,自己感觉以前学的还是太浅,再展开学学吧。

今天先从考的比较频繁的“多线程共享数据”开始。

一.目标

谈到多线程共享数据,理想情况下我们希望做到“同步”“互斥”。这是目标我们暂且把它先放到这。

二.分类

多线程共享数据通常的场景有一下两种:

场景一:

卖票,我们都买过火车票。要买火车票我们可以去车站,也可以通过代售点(或网购),但不管有多少种方式火车票的总数是一定的。

场景抽象:

对于卖票系统每个线程的核心执行的代码都相同(就是票数--)。

解决方法:

只需创建一个Runnable,这个Runnable里有那个共享数据。

代码模拟:

 

package 多线程共享数据;

public class Ticket implements Runnable{

	private int ticket = 10;
	public void run() {
		while(ticket>0){
			ticket--;
			System.out.println("当前票数为:"+ticket);
		}
		
	}

}

 

package 多线程共享数据;

public class SellTicket {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Ticket t = new Ticket();
		new Thread(t).start();
		new Thread(t).start();
	}

}

 场景二:比较常见的例子,银行问题,我们对账户可以存钱也可以取钱,怎么保证这样的数据共享呢?

场景抽象:

每个线程执行的代码不同(比如上面的问题,对每个账户可以执行++操作和--操作),这时候需要用不同的Runnable对象,有如下两种方式来实现这些Runnable之间的数据共享
解决方案:
有两种方法来解决此类问题:
  • 将共享数据封装成另外一个对象中封装成另外一个对象中,然后将这个对象逐一传递给各个Runnable对象,每个线程对共享数据的操作方法也分配到那个对象身上完成,这样容易实现针对数据进行各个操作的互斥和通信
  • 将Runnable对象作为偶一个类的内部类,共享数据作为这个类的成员变量,每个线程对共享数据的操作方法也封装在外部类,以便实现对数据的各个操作的同步和互斥,作为内部类的各个Runnable对象调用外部类的这些方法。
代码模拟:
以一道面试题为例:
“设计4个线程。,其中两个线程每次对j增加1,另外两个线程对j每次减1”
(第一种解法)
public class MyData {
	 private int j=0;
	 public  synchronized void add(){
		 j++;
		 System.out.println("线程"+Thread.currentThread().getName()+"j为:"+j);
	 }
	 public  synchronized void dec(){
		 j--;
		 System.out.println("线程"+Thread.currentThread().getName()+"j为:"+j);
	 }
	 public int getData(){
		 return j;
	 }
}
 
public class AddRunnable implements Runnable{
	MyData data;
	public AddRunnable(MyData data){
		this.data= data;
	}

	
	public void run() {
		
			data.add();
	
		
	}

}
 

 

 

public class DecRunnable implements Runnable {
	MyData data;
	public DecRunnable(MyData data){
		this.data = data;
	}
	public void run() {
	
			data.dec();
		
	}

}

 测试代码:

 

 

public class TestOne {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		MyData data = new MyData();
		Runnable add = new AddRunnable(data);
		Runnable dec = new DecRunnable(data);
		for(int i=0;i<2;i++){
			new Thread(add).start();
			new Thread(dec).start();
		}
	}

 解法分析:

优点:

 

           1.这种解法代码写的有条理,简单易读,从main中很容易整理出思路

           2.将数据抽象成一个类,并将对这个数据的操作作为这个类的方法,这么设计可以和容易做到同步,只要在方法上加”synchronized“

不足:

           代码写的比较繁琐,需要有多个类,不是那么简洁

个人观点:为了有条理个人比较喜欢这种写法。

(第二种解法)

 

public class MyData {
	 private int j=0;
	 public  synchronized void add(){
		 j++;
		 System.out.println("线程"+Thread.currentThread().getName()+"j为:"+j);
	 }
	 public  synchronized void dec(){
		 j--;
		 System.out.println("线程"+Thread.currentThread().getName()+"j为:"+j);
	 }
	 public int getData(){
		 return j;
	 }
}

 

public class TestThread {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		final MyData data = new MyData();
		for(int i=0;i<2;i++){
			new Thread(new Runnable(){

				public void run() {
					data.add();
				
				}
			
			}).start();
			new Thread(new Runnable(){

			
				public void run() {
					data.dec();
				
				}
			
			}).start();
		}
	}

}

 解法分析:

与第一种方法的区别在于第二种方法巧妙的用了内部类共享外部类数据的思想,即把要共享的数据变得全局变量,这样就保证了操作的是同一份数据。同时内部类的方式使代码更加简洁。但是不如第一种解法条理那么清楚。

结束语:感谢网上各种资料,只要想学网上的各种资料是这么丰富。学无止境,少年加油!!!

 

 

分享到:
评论
4 楼 absurd1350 2017-02-22  
superhotdong 写道
第一个卖票系统那个场景的实现方式不行的,ticket--不是原子操作,这个程序跑起来会有问题,会出现一张票被多个网点卖出去的情况,需要做同步。

用AtomicInteger。
3 楼 kutygou 2016-03-10  
2楼说的是正确的
2 楼 superhotdong 2015-07-23  
第一个卖票系统那个场景的实现方式不行的,ticket--不是原子操作,这个程序跑起来会有问题,会出现一张票被多个网点卖出去的情况,需要做同步。
1 楼 javafound 2013-05-14  
顶!学无止境,少年加油!!!

相关推荐

    qt线程共享数据 信号和槽方式

    即使用一个两个线程都能够共享的变量(如全局变量),这样两个线程都能够访问和修改该变量,从而达到共享数据的目的。 Qt 线程间共享数据是本文介绍的内容,多的不说,先来啃内容。Qt线程间共享数据主要有两种方式...

    针对于Executor框架,Java API,线程共享数据

    Executor框架是Java并发编程中的一个重要工具,它提供了一种管理线程池的方式,使得我们可以更方便地管理线程的生命周期和执行线程任务。 原子操作是指不可被...在两个线程之间共享数据,可以通过以下几种方式实现:

    共享内存多线程数据交换(C++)

    前几天学习共享内存,和多线程应用写了个小程序,给初学者一点帮助

    Qt多线程通信 附源码demo

    即使用一个两个线程都能够共享的变量(如全局变量),这样两个线程都能够访问和修改该变量,从而达到共享数据的目的。 2)使用singal/slot机制,把数据从一个线程传递到另外一个线程。 代码中是针对信号和槽机制,...

    C#的多线程示例;几个多线程之间的互斥,同步;WPF主界面INVOKE

    几个多线程之间的互斥,同步;WPF主界面

    C#多线程互斥实例 多线程获取同一变量

    C#多线程互斥实例 多线程获取同一变量(不重复)。是一个很好的学习例子

    C# socket多线程编程

    所谓单个写入程序/多个阅读程序的线程同步问题,是指任意数量的线程访问共享资源时,写入程序(线程)需要修改共享资源,而阅读程序(线程)需要读取数据。在这个同步问题中,很容易得到下面二个要求: 1) 当一个...

    Linux下的C语言多线程编程

    而运行于一个进程中的多个线程,它们彼此之间使用相同的地址 空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间...

    qt 多线程共享全局缓冲区

    文章地址[https://blog.csdn.net/MMTS_yang/article/details/110071610], 不必再这里下载,环境qt5.12.3

    Java多线程编程 线程同步机制.docx

    线程安全问题的产生是因为多个线程并发访问共享数据造成的,如果能将多个线程对共享数据的并发访问改为串行访问,即一个共享数据同一时刻只能被一个线程访问,就可以避免线程安全问题。锁正是基于这种思路实现的一种...

    C Socket通信多线程数据双向收发实例VS2010(全部源码)

    1、运用多线程和Socket技术实现Socket Server端侦听多个客户端请求; 2、实现服务器端循环处理客户端不同请求从而实现不同测试要求,并向客户端循环发送数据; 3、实现客户端向服务器端发送不同测试命令,并接收...

    Linux下多线程编程详解

    而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。...

    使用Java多线程共享数据结构需注意的几个问题.pdf

    #资源达人分享计划#

    92道Java多线程与并发面试题含答案(很全)

    同步(Synchronization):同步是控制多个线程访问共享资源的方式,以防止数据不一致和竞态条件。Java提供了多种同步机制,包括synchronized关键字、Lock接口和Semaphore类。 线程间通信(Inter-Thread ...

    java多线程安全性基础介绍.pptx

    多个线程操作共享对象导致的状态不一致问题 原因 共享资源的竞态条件问题 问题 竞态条件 指令重排序 工作内存中主内存同步延迟 解决 要有安全策略文档或注释 不共享 线程封闭 仅在单线程内访问数据 栈...

    Java多线程共享数据、同步、通信

    一、线程共享数据  a)继承Thread,那么我们可以创建很多个这样的类,但是每个这样的类都是相互不关联的,也是说我们Thread类中的内容每个创建出来的类都有一份,因此它不适合作为数据共享的线程来操作。同时由于...

    C++多线程PPT和源码.rar

    而如果在一个进程中用多线程,彼此之间使用相同的地址空间,共享数据,线程切换的代价很小。多编程并发在企业中开发显得尤为重要,本课程包含Windows多线程编程与C++11高并发编程,通过浅显易懂的代码与讲解,让你的...

    linux中一个程序的两个线程的同步(c语言实现)

    两个线程共享变量a,一个负责加一,一个负责输出 通过信号灯的pv操作完成

    VC模拟实现管道缓存,可多线程共享数据。

    做多媒体应用,经常需要在各线程间共享数据,本例子介绍了一种简便的缓存管理办法。 特点: 1. 初始申请一个缓存区,模拟管道操作,从末尾写入,从开头读取。 2. 线程安全,写入,读取块大小无限制。 3. 代码简单,...

Global site tag (gtag.js) - Google Analytics