`
shuofenglxy
  • 浏览: 190731 次
  • 性别: 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。呵呵~

相关推荐

Global site tag (gtag.js) - Google Analytics