`

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

阅读更多

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线程间共享数据主要有两种方式...

    winform 多线程处理数据

    - 为避免资源竞争,确保线程安全,特别是在多个线程访问共享数据时。 通过理解和应用这些概念,开发者可以构建出高效且用户体验良好的WinForm应用,即使在处理大量数据时也能保持界面的响应性。

    MFC 多线程之间通过消息传递数据

    5. **注意线程安全**: 尽量避免在一个线程中修改另一个线程的数据,除非在进行同步操作。如果必须这样做,确保使用线程安全的数据结构或同步原语。 6. **性能考虑**: 虽然消息传递提供了良好的线程间通信方式,但...

    多线程之间的线程通信

    "多线程之间的线程通信"是确保多个线程协同工作、避免数据不一致性和提高程序效率的关键概念。在本话题中,我们将深入探讨线程通信的原理、方法,以及潜在的危险。 首先,线程通信是指在一个进程中,不同的线程之间...

    易语言多线程传递自定义数据(免加延时方法)

    这种方式需要注意线程同步问题,以防止多个线程同时读写同一数据导致的数据冲突。易语言提供了“互斥量”、“信号量”等同步原语,用于控制对共享资源的访问。 在多线程编程中,避免使用延时方法是非常重要的,因为...

    多线程导入excel 数据

    - **并发策略**:可以采用“任务分割”策略,将大文件拆分成多个小任务,每个任务由一个线程处理。通过`ExecutorService`提交任务到工作队列,实现并行处理。 - **线程池管理**:使用`ExecutorService`可以更有效...

    NET中多线程间资源共享与访问

    在多线程环境中,多个线程可能需要同时访问相同的资源,例如内存变量、文件系统或数据库连接等。如果不加以控制,这种并发访问可能会导致数据不一致和其他问题,如死锁、竞态条件等。因此,实现线程间的资源共享和...

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

    在这个"多线程互斥实例 多线程获取同一变量"的示例中,我们将探讨如何在多个线程中安全地访问共享资源,避免数据不一致性和竞态条件。 首先,我们需要理解多线程中的一些核心概念: 1. **线程**:线程是操作系统...

    qt 多线程共享全局缓冲区

    另外,Qt还提供了QSharedMemory和QSystemSemaphore等高级同步机制,适用于跨进程的共享数据。这些工具可以帮助你在多进程环境中实现全局缓冲区的共享,但要注意它们的使用方式和线程安全问题。 总结来说,Qt 5.12.3...

    Delphi多个DLL共享全局数据Demo

    5. **线程同步**:当多个线程可能同时访问共享数据时,必须使用线程同步机制,如互斥量(Mutex)、信号量(Semaphore)或事件对象(Event),以防止数据竞争和不一致。 6. **导出和导入函数**:在DLL中,需要使用`...

    线程间通信方式3:消息传递方式

    消息传递可以作为避免数据竞争的一种手段,因为每个线程通过消息队列顺序地接收和处理消息,而不是直接访问共享数据。 - 但需要注意的是,如果消息涉及到共享资源,仍需使用互斥量或临界区来保护这些资源。 6. **...

    java多线程处理数据库数据

    线程是程序执行的最小单位,一个进程可以包含多个线程。在Java中,我们可以使用`Thread`类或者`Runnable`接口来创建线程。然而,为了更好地管理和控制线程,Java并发包提供了如`ExecutorService`、`Future`、`...

    多线程操作共享文件.zip

    多线程使得程序能够同时执行多个任务,但当多个线程试图访问同一资源(如文件)时,可能会引发竞态条件,导致数据不一致或错误。因此,必须采取适当的同步机制来防止这种情况。 描述中的“delphi多线程互斥操作共享...

    多线程同步大量数据转录的多线程和同步

    这种机制在处理大量数据转录时尤为有用,因为它允许多个线程并行读取数据,而写入操作则需要独占锁,以确保数据的一致性。 ### 队列(Queue)作为数据结构 在实现多线程数据转录的过程中,队列(`Queue&lt;T&gt;`)被...

    多线程资源共享集合

    然而,当多个线程访问和修改同一资源时,可能会引发数据不一致、竞态条件等问题,这就需要我们进行资源共享的管理。 `synchronized`关键字是Java中用来控制多线程并发访问共享资源的一种机制。它提供了互斥访问,...

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

    这防止了多个线程对同一资源的竞态条件,确保数据的一致性。 同步则是控制线程执行顺序和协调的方式。C#提供了多种同步机制,如`Monitor`、`Semaphore`和`Mutex`等。其中,`Monitor`使用了.NET框架的锁对象,可以...

    易语言线程返回数据的方法

    3. **共享数据结构**:线程可以将结果存入一个共享数据结构(如数组或链表),然后通过同步机制(如锁)通知其他线程数据已准备好。 4. **使用线程句柄**:易语言允许通过线程句柄发送消息,线程执行完毕后,可以...

    进程和线程之间的关系

    在同一个进程中,多个线程共享该进程的所有资源(如内存空间、打开的文件等),但每个线程都有自己独立的栈空间来保存函数调用时的信息。由于线程之间共享相同的资源,因此它们之间的通信非常便捷,且创建和切换的...

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

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

Global site tag (gtag.js) - Google Analytics