java并发深入

来源:互联网 发布:话费充值平台源码 编辑:程序博客网 时间:2024/05/22 14:27
对象由数据+行为组成。数据就是字段,行为就是方法。
并发需要保证这些可被多个线程访问的共享对象数据的完整性,以及某些特定完整性语义。
比如一个类有一个字段count=0,两个线程同时对它做加1操作。
这时就有可能发生:
 线程1查询到count为1对其加1。
 线程2查询到count为1,对其加1。

  接着线程1提交,线程2提交。
最终值count还是为1。

也就是说线程1对count的修改丢失了。

解决这个问题,需要加锁。
java提交了内置锁syncronized,以及Lock。
内置锁syncronized,利用monitorEnter及monitorExit两条指令保证数据的可见性与原子性。

比如A类有一个字段count,默认值为0,代码如下:
public class A{
  private int count=0;
 
  public syncronized add(){
     count++;
  }
}

线程一首先调用add方法,这时会发生以下步骤:
1.线程二尝试获取在当前A实例上的锁,没有获取到则阻塞
2.获取到锁后,将count值从主存拷到当前线程工作内存,这时count为0

线程二这时执行add方法,但发现获取不到锁,这时阻塞在那边。

线程一执行完加1后,退出解锁。
这时线程二就可以获取到锁了。

并发中对于某些集合,要使它成为同步类,我们常常使用封装,如下:
class SyncMap{
  Map<String,String> maps=new HashMap<String,String>();
 
  public syncronized V put(K key, V value){
    maps.put(key,value);
  }
}

这样做的优点是不管底层maps有无同步,同步策略是什么,都可以安全的实现同步。

还有一种实现同步的方法,即将需要同步的操作交由已经存在的同步类来做。
考虑上面的count加1操作,如果将count类型改成AtomicInteger,由AtomicInteger实现同步,原子加1操作。

atomic
===============atomic all finish,cost:247,the res:3000000
===============atomic all finish,cost:248,the res:3000000
===============atomic all finish,cost:262,the res:3000000
===============atomic all finish,cost:239,the res:3000000
===============atomic all finish,cost:249,the res:3000000

sync
===============sync all finish,cost:54,the res:3000000
===============sync all finish,cost:45,the res:3000000
===============sync all finish,cost:47,the res:3000000
===============sync all finish,cost:45,the res:3000000
===============sync all finish,cost:49,the res:3000000

测试表明上述对于300个线程,每个线程做10000次加1操作,内置锁syncronized比atomicInteger效率要高

基本上当并发竞争某个锁很激烈时,内置锁或者Lock比CAS效率高,原因是当竞争很激烈时,多个线程做CAS时发现很难成功,这样浪费了很多CPU资源。

测试代码如下:

public class SyncWithAtomicTest {private int count=0;private static final int threadCount=300;private static final int countNum=10000;private final AtomicInteger countAtomicInteger=new AtomicInteger(0);private static final ExecutorService threadPool=Executors.newFixedThreadPool(threadCount);private final CountDownLatch latchStart=new CountDownLatch(threadCount);private final CountDownLatch latchEnd=new CountDownLatch(threadCount);public synchronized void addWithCountSync(){for(int i=0;i<countNum;i++){count++;}}public void addWithAtomicCount(){for(int i=0;i<countNum;i++){countAtomicInteger.incrementAndGet();}}public static void main(String[] args) throws InterruptedException {SyncWithAtomicTest obj=new SyncWithAtomicTest();Long oldTime=System.currentTimeMillis();for(int i=0;i<threadCount;i++){CountTask t=new CountTask();t.setTarget(obj);threadPool.execute(t);}obj.latchEnd.await();Long endTime=System.currentTimeMillis()-oldTime;//System.out.println("===============atomic all finish,cost:"+endTime+",the res:"+obj.countAtomicInteger.get());System.out.println("===============sync all finish,cost:"+endTime+",the res:"+obj.count);}static class CountTask implements Runnable{private SyncWithAtomicTest target;public void run() {try {target.latchStart.countDown();target.latchStart.await();//we do add oper when all threads is ready target.addWithCountSync();//target.addWithAtomicCount();System.out.println("thread:"+Thread.currentThread().getId()+",finish the work");target.latchEnd.countDown();} catch (InterruptedException e) {e.printStackTrace();Thread.currentThread().interrupt();}}public void setTarget(SyncWithAtomicTest target) {this.target = target;}}}




1 0
原创粉丝点击