`
shuofenglxy
  • 浏览: 194471 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

多线程之数据同步方式

阅读更多
多线程出现后,最大的问题就是对资源的竞争如何保证同步的状态。java中同步的办法有很多。通过以下几个代码示例来看JAVA多线程同步状态保持机制。

首先来看无同步状态下 多线程和单线程执行情况,代码示例如下:
package sychonizedDemo;

import java.util.concurrent.CountDownLatch;

public class NomalSeqTask {

	public static void main(String[]args){
		CountDownLatch doneSignal = new CountDownLatch(20);
		AddOne addOne = new AddOne("addOne",doneSignal);
		System.out.println("Result  of addOne Thread is as follows");
		
		for(int i=0;i<20;i++)
			new Thread(addOne).start();
		try {
			doneSignal.await();
		} catch (InterruptedException e) {

		}
		System.out.println();
		System.out.println("Result  of addOneInSeq Thread is as follows");
		AddOneInSeq addOneInSeq = new AddOneInSeq("addOneInSeq");
		for(int i=0;i<20;i++)
			addOneInSeq.add();
	}
}
class AddOne extends Thread{
	private static int count;
	private CountDownLatch doneSignal;
	public AddOne(String name,CountDownLatch doneSignal) {
		super(name);
		this.doneSignal =  doneSignal;
	}

	public void add(){
		count++;
		try {
			this.sleep(1000L);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.print(count +" ");
		doneSignal.countDown();
	}
	
	public void run(){
		add();
	}
}

class AddOneInSeq  {
	private String name;
	private static int count;
	public AddOneInSeq(String name) {
		this.name = name;
	}

	public void add(){
		count++;
		System.out.print(count+" ");
	}
}




执行结果如下:

Result  of addOne Thread is as follows
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
Result  of addOneInSeq Thread is as follows
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

原因分析:
由于多个线程共同访问AddOne中的count,这时候由于对资源的访问没有限制所以造成了数据上的不一致性,本来多个线程期望看到的结果都是自己的加1后结果,实际确如打印所以。而单线程的打印结果却是一致的。这里就突出了多线程引用时数据一致性的问题,也就是所谓的状态同步。

解决方法大致有以下几种:
第一,加上外观锁。

具体代码示例如下:

package sychonizedDemo;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockDemo {

	public static void main(String[]args){
		
		System.out.println("Result  of addOneThreadSafeByLock Thread is as follows");
		AddOneThreadSafeByLock addOneThreadSafeByLock = new AddOneThreadSafeByLock("addOneThreadSafeByLock");
		for(int i=0;i<20;i++)
			new Thread(addOneThreadSafeByLock).start();
	
	}
}

class AddOneThreadSafeByLock extends Thread{
	private static int countThreadSafe;
	private Lock lock = new ReentrantLock();
	
	public AddOneThreadSafeByLock(String name) {
		super(name);
	}

	public  void add(){
		lock.lock();
		try{
			countThreadSafe++;
			System.out.println(countThreadSafe);
		}catch(Exception e){
			lock.unlock();
		}finally{
			lock.unlock();
		}
	}
	
	public void run(){
		add();
	}
}


执行结果如下:

Result  of addOneThreadSafeByLock Thread is as follows
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

第二,加上synchronized。
package sychonizedDemo;

public class SynchronizedMethod {

	public static void main(String[]args){
		
		System.out.println("Result  of addOneThreadSafe Thread is as follows");
		AddOneThreadSafe addOneThreadSafe = new AddOneThreadSafe("addOneThreadSafe");
		for(int i=0;i<20;i++)
			new Thread(addOneThreadSafe).start();
	}
}

class AddOneThreadSafe extends Thread{
	private static int countThreadSafe;
	
	public AddOneThreadSafe(String name) {
		super(name);
	}

	public  synchronized void add(){
		countThreadSafe++;
		System.out.println(countThreadSafe);
	}
	
	public void run(){
		add();
	}
}


执行结果同上。


第三,原子操作类。代码示例如下:
package sychonizedDemo;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

// 并不能保证连续返回的都是1-20

/**
 * @author Administrator
 * Atomic: volatile +CAS
 * situable for mild and moderate competition
 */
public class AtomicDemo {

	public static void main(String[]args){
		
		System.out.println("Result  of addOneThreadSafeByAtomicInteger Thread is as follows");
		CountDownLatch doneSignal = new CountDownLatch(20);
		AddOneThreadSafeByAtomicInteger addOneThreadSafeByAtomicInteger = new AddOneThreadSafeByAtomicInteger("addOneThreadSafeByAtomicInteger",doneSignal);
		Thread t = null;
		for(int i=0;i<20;i++){
			t = new Thread(addOneThreadSafeByAtomicInteger);
			t.setName("addOneThreadSafeByAtomicInteger-"+i);
			t.start();
		}
		try {
			doneSignal.await();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		for(String name : addOneThreadSafeByAtomicInteger.getResultMap().keySet()){
			System.out.println(name+ " value is  "+addOneThreadSafeByAtomicInteger.getResultMap().get(name));
		}
	}
}

class AddOneThreadSafeByAtomicInteger extends Thread{
	private static AtomicInteger countAtomicInteger = new AtomicInteger(0);
	private static Map<String,Integer> resultMap = new ConcurrentHashMap<String,Integer>();
	public Map<String, Integer> getResultMap() {
		return resultMap;
	}

	public static void setResultMap(Map<String, Integer> resultMap) {
		AddOneThreadSafeByAtomicInteger.resultMap = resultMap;
	}

	private CountDownLatch doneSignal;
	public static AtomicInteger getCountAtomicInteger() {
		return countAtomicInteger;
	}

	public static void setCountAtomicInteger(AtomicInteger countAtomicInteger) {
		AddOneThreadSafeByAtomicInteger.countAtomicInteger = countAtomicInteger;
		
	}

	public AddOneThreadSafeByAtomicInteger(String name,CountDownLatch doneSignal) {
		super(name);
		this.doneSignal = doneSignal;
	}

	public  int addOneByAtomicInteger(){
		//循环    CAS 返回
		for(;;) {
			int current = countAtomicInteger.get();
			int next = current+1;
			if(countAtomicInteger.compareAndSet(current, next)) 
				return next;
			
		}
		
	}
	
	public void run(){
		resultMap.put(this.getName()+doneSignal.getCount(), this.addOneByAtomicInteger());
		doneSignal.countDown();
	}
}



结果如下:
Result  of addOneThreadSafeByAtomicInteger Thread is as follows
addOneThreadSafeByAtomicInteger15 value is  6
addOneThreadSafeByAtomicInteger16 value is  5
addOneThreadSafeByAtomicInteger3 value is  18
addOneThreadSafeByAtomicInteger4 value is  17
addOneThreadSafeByAtomicInteger14 value is  7
addOneThreadSafeByAtomicInteger9 value is  12
addOneThreadSafeByAtomicInteger2 value is  19
addOneThreadSafeByAtomicInteger12 value is  9
addOneThreadSafeByAtomicInteger8 value is  13
addOneThreadSafeByAtomicInteger7 value is  15
addOneThreadSafeByAtomicInteger13 value is  8
addOneThreadSafeByAtomicInteger10 value is  11
addOneThreadSafeByAtomicInteger20 value is  1
addOneThreadSafeByAtomicInteger5 value is  16
addOneThreadSafeByAtomicInteger19 value is  2
addOneThreadSafeByAtomicInteger11 value is  10
addOneThreadSafeByAtomicInteger18 value is  3
addOneThreadSafeByAtomicInteger1 value is  20
addOneThreadSafeByAtomicInteger17 value is  4

原因分析:由于原子操作类没有采取起始锁资源的方式,所以与前两种方式比较,它执行的结果保证了数据的一致性,却不保证线程执行的顺序性(大致原因比如在cas时,2号线程与1号线程冲突,这时候重复循环,但此时3号线程刚好CAS为真了,所以3号线程就可能先执行我拿了,还有就是在加入结果map时,如果不幸的1号线程正锁住了一个小区在添加,2号线程也恰好杯具的映射到这个小区,那么2号结果就要在后加入了)。这种操作的优势在于,减少了锁资源的开销,有利于并发。而第一,第二种方式都是锁住了资源导致,在共享这个临界资源时是一个排队使用的情况,这里就有可能成为并发性能的瓶颈之一了。

第四种,volatile方式。

具体代码示例如下:
package sychonizedDemo;

public class VolatileDemo {

	public static void main(String[] args){
		AndOneByVolatile andOneByVolatile = new AndOneByVolatile("AndOneByVolatile");
		Thread t = null;
		for(int i=0;i<20;i++){
			t = new Thread(andOneByVolatile);
			t.setName("andOneByVolatile-"+i);
			t.start();
		
		}
	}
}

class AndOneByVolatile extends Thread{
	private String name;
	public  static  volatile int  countVolatile;
	public AndOneByVolatile(String name) {
		this.name = name;
	}
	public void add(){
		countVolatile++;
		System.out.print(countVolatile+" ");
	}
	public void run(){
		add();
	}
	
}


执行结果略,使用与状态之类改变能为其他线程所知,此关键字相当于将线程的调用设置为引用,改的都是同一个值所以就会出现高并发下,一个线程由 500 +1 另外一个线程也由500+1最终造成累加结果与预期不一致的情况。


总结:
Lock  :显示的外部加锁,可以有多个condition.
sychronized :Lock的简化版。保持资源。
volatile:保证了更改状态的可见性。
atomic类:通过volatile+cas的方式维持了一致性。适用于轻度和中度锁竞争的场合。


0
0
分享到:
评论
2 楼 shuofenglxy 2011-03-02  
酒杯中的大海 写道
第四种,volatile方式。 明显不能得到1和2的结果。直接把for(int i=0;i<20;i++)加到循环1000,就出现问题了。public void add()必须是synchronized。呵呵~

表述上有点问题   我改一下哈   那个意思是只能保证可见性  文字已经说明了  不过还是3x你的答复
1 楼 酒杯中的大海 2011-03-02  
第四种,volatile方式。 明显不能得到1和2的结果。直接把for(int i=0;i<20;i++)加到循环1000,就出现问题了。public void add()必须是synchronized。呵呵~

相关推荐

    多线程数据同步

    标题"多线程数据同步"直指这一核心问题,而描述则具体提到了使用临界区对象作为解决方案之一。 线程同步是为了防止多个线程同时访问共享资源,导致数据的混乱。在Windows操作系统中,临界区对象是一种轻量级的同步...

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

    在实现多线程数据转录的过程中,队列(`Queue&lt;T&gt;`)被用作数据结构来存储待处理的数据。队列是一种先进先出(FIFO)的数据结构,非常适合用于处理多线程环境中的任务调度。写入线程将数据添加到队列的尾部,而读取...

    3种多线程实现同步方法

    然而,多线程也带来了数据同步的问题,因为多个线程可能同时访问共享资源,如果不加以控制,就可能导致数据不一致或引发错误。本篇文章将深入探讨三种在C++中实现多线程同步的方法:事件对象、关键代码段和互斥对象...

    Core Data多线程大量数据同步

    ### Core Data 多线程大量数据同步详解 #### 前言 在iOS开发中,Core Data 是一种广泛使用的持久层框架,它提供了一种面向对象的方式来存储和管理应用程序的数据。随着应用程序复杂度的增加,如何高效、稳定地管理...

    多线程临界段同步演示1

    然而,多线程编程也带来了一些挑战,其中之一就是如何确保线程安全,即在多线程环境下正确地共享数据。这里我们将深入探讨"多线程临界段同步"的概念,以及如何通过API方式实现它,而不是依赖MFC(Microsoft ...

    线程同步的四种方式

    在多线程编程中,线程同步是一种控制多个线程并发执行时访问共享资源的方式,以避免数据不一致和死锁等问题。以下是对线程同步的四种主要方式的详细解释: 1. **事件(Event)** 事件是Windows API提供的一种线程...

    某电信项目多线程同步数据实例

    在提供的"ScanThread"文件名中,我们可以推测这是一个扫描线程的实现,可能是用来定时或按需扫描数据源,然后触发数据同步的过程。可能包含了线程启动、同步机制、数据处理逻辑等内容。 综上所述,这个电信项目实例...

    多线程导入excel 数据

    综上所述,多线程导入Excel数据是一个涉及并发控制、线程同步、数据处理和性能优化的复杂过程。在具体实现时,要结合项目需求和资源限制,选择合适的策略和技术。例如,文件`BigdataTest.java`可能是实现上述功能的...

    Delphi多线程同步的例子

    在编程领域,多线程是实现并发执行任务的重要方式,特别是在 Delphi 这样的面向对象的编程环境中。本文将深入探讨Delphi中的多线程和线程同步,并以"SortThreads"和"delphi-thread-gui"这两个示例项目为例,讲解如何...

    linux上实现多进程和多线程实现同步互斥(源代码)

    多线程则是在一个进程中创建多个执行流,它们共享同一块内存空间,资源利用率高,但数据同步和互斥问题更复杂。在Linux中,可以使用`pthread_create()`创建线程,`pthread_join()`等待线程结束。线程间的同步互斥...

    c++实现多线程同步

    总之,C++的多线程功能使得开发者能够充分利用现代硬件的并行处理能力,而信号量作为一种有效的同步工具,可以防止数据竞争,确保程序的正确性。理解并掌握这些概念对于编写高效、可靠的多线程程序至关重要。

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

    总结起来,C#的多线程机制允许我们创建并行执行的任务,通过线程互斥和同步保证数据一致性。而在WPF中,我们需谨慎处理UI更新,利用`Dispatcher`确保操作在正确的线程上执行。理解并熟练运用这些概念和技术,对于...

    VC++多线程同步基本示例

    在编程领域,尤其是在Windows平台下开发C++应用时,多线程技术是非常关键的一部分,它允许程序同时执行多个任务,从而提升系统效率。...因此,对多线程同步的理解和应用能力是每个专业程序员必备的技能之一。

    qt udp多线程收发数据

    基于多线程的QUdpSocket收发数据程序,界面上可以输入目标ip、port,与网络调试助手调试ok 欢迎下载,并指出程序中的问题,谢谢

    Powerbuilder中实现多线程同步查询数据 源程序

    在PowerBuilder中实现多线程同步查询数据是一项高级技术,涉及到并发编程和数据库访问的优化。PowerBuilder是一款强大的第四代编程语言(4GL),尤其适用于开发数据库应用系统。本源程序的目标是提高应用程序的性能...

    delphi 多线程 读取数据

    在IT领域,多线程是一种常见的编程技术,用于提高程序的执行效率,特别是在处理大量数据时。本示例中,我们关注的是如何在Delphi环境中使用TThread组件进行多线程编程,以便并行读取文本文件数据。下面将详细阐述这...

    操作系统实验多线程同步(含C++源代码)

    操作系统中的多线程同步是一个关键概念,特别是在并发编程中,它涉及到如何协调多个线程以避免数据不一致性和竞态条件。在这个实验中,我们关注的是C++编程语言中的实现,以及操作系统如何处理线程的优先级。 首先...

    多线程及线程同步

    然而,多线程环境下也带来了一些问题,尤其是资源竞争和数据一致性问题,这些问题需要通过线程同步机制来解决。本文将详细介绍如何通过临界区、互斥内核对象、事件内核对象和信号量内核对象来实现线程同步。 1. ...

    多线程的同步机制 VC++

    多线程同步机制在软件开发中扮演着至关重要的角色,特别是在多处理器系统或者并发执行的任务中,确保线程间的正确协作和数据一致性是必不可少的。VC++中提供了多种同步机制来处理多线程间的同步问题,其中Event是...

    mybaits 多线程 实现数据批量插入 (运用CountDownLatch实现闭锁)

    本文将详细介绍如何利用MyBatis结合多线程和CountDownLatch闭锁来实现数据的批量插入。 首先,我们来看`mybatis批处理`。MyBatis的批处理功能允许我们在一次数据库连接中执行多条SQL语句,从而减少了数据库连接的...

Global site tag (gtag.js) - Google Analytics