对象由数据+行为组成。数据就是字段,行为就是方法。

并发须要保证这些可被多个线程訪问的共享对象数据的完整性,以及某些特定完整性语义。

比方一个类有一个字段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;
}
} }

java并发深入的更多相关文章

  1. 多线程的通信和同步(Java并发编程的艺术--笔记)

    1. 线程间的通信机制 线程之间通信机制有两种: 共享内存.消息传递.   2. Java并发 Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式执行,通信的过程对于程序员来说是完全透 ...

  2. 【Java并发编程实战】----- AQS(四):CLH同步队列

    在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形.其主要从两方面进行了改造:节点的结构与节点等待机制.在结构上引入了头 ...

  3. 【Java并发编程实战】----- AQS(三):阻塞、唤醒:LockSupport

    在上篇博客([Java并发编程实战]----- AQS(二):获取锁.释放锁)中提到,当一个线程加入到CLH队列中时,如果不是头节点是需要判断该节点是否需要挂起:在释放锁后,需要唤醒该线程的继任节点 ...

  4. 【Java并发编程实战】----- AQS(二):获取锁、释放锁

    上篇博客稍微介绍了一下AQS,下面我们来关注下AQS的所获取和锁释放. AQS锁获取 AQS包含如下几个方法: acquire(int arg):以独占模式获取对象,忽略中断. acquireInte ...

  5. 【Java并发编程实战】-----“J.U.C”:CLH队列锁

    在前面介绍的几篇博客中总是提到CLH队列,在AQS中CLH队列是维护一组线程的严格按照FIFO的队列.他能够确保无饥饿,严格的先来先服务的公平性.下图是CLH队列节点的示意图: 在CLH队列的节点QN ...

  6. 【Java并发编程实战】-----“J.U.C”:CountDownlatch

    上篇博文([Java并发编程实战]-----"J.U.C":CyclicBarrier)LZ介绍了CyclicBarrier.CyclicBarrier所描述的是"允许一 ...

  7. 【Java并发编程实战】-----“J.U.C”:CyclicBarrier

    在上篇博客([Java并发编程实战]-----"J.U.C":Semaphore)中,LZ介绍了Semaphore,下面LZ介绍CyclicBarrier.在JDK API中是这么 ...

  8. 【Java并发编程实战】-----“J.U.C”:ReentrantReadWriteLock

    ReentrantLock实现了标准的互斥操作,也就是说在某一时刻只有有一个线程持有锁.ReentrantLock采用这种独占的保守锁直接,在一定程度上减低了吞吐量.在这种情况下任何的"读/ ...

  9. Java并发编程:volatile关键字解析

    Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在 ...

  10. JAVA并发编程J.U.C学习总结

    前言 学习了一段时间J.U.C,打算做个小结,个人感觉总结还是非常重要,要不然总感觉知识点零零散散的. 有错误也欢迎指正,大家共同进步: 另外,转载请注明链接,写篇文章不容易啊,http://www. ...

随机推荐

  1. 用swift开发仪表盘控件(二)

    二.代码分析 这个控件本质就是从UIView继承的一个类而已.所以整个代码事实上就是一个定制的UIView类. 依据UIView的规则进行例如以下初始化: required init(coder aD ...

  2. [Kernel]理解System call系统调用

    转自:http://os.51cto.com/art/200512/13510.htm 现在,您或许正在查看设备驱动程序,并感到奇怪:“函数 foo_read() 是如何被调用的?”或者可能疑惑: “ ...

  3. 为什么对一些矩阵做PCA得到的矩阵少一行?

    很多时候会出现把一个N*M的矩阵做pca(对M降维)之后却得到一个M*(M-1)矩阵这样的结果.之前都是数学推导得到这个结论,但是, 今天看到一个很形象的解释: Consider what PCA d ...

  4. Lua中的常用语句结构以及函数

     1.Lua中的常用语句结构介绍 --if 语句结构,如下实例: gTable = {} ] ] then ]) == gTable[] then ]) else print("unkown ...

  5. Insubstantial 6.2 Release

    http://shemnon.com/speling/2011/04/insubstantial-62-release.html

  6. 第二百五十一节,Bootstrap项目实战--响应式轮播图

    Bootstrap项目实战--响应式轮播图 学习要点: 1.响应式轮播图 本节课我们要在导航条的下方做一张轮播图,自动播放最新的重要动态. 一.响应式轮播图 响应式轮播图 第一步,设置轮播器区域car ...

  7. 原生YII2 增删改查的一些操作(非ActiveRecord)

    1.添加数据 如下,使用insert方法:t_admin_user为数据表名..其他的是属性.. $num = Yii::$app->db->createCommand()->ins ...

  8. UEditor API 文档

    来源:http://www.e4dai.com/ueditor-api/#ue.editor http://www.e4dai.com/ueditor-api/ UE.Editor 依赖 editor ...

  9. 修改Android 界面颜色

    btnGetCode.setTextColor(getResources().getColor(R.color.dark_white)); Color.parseColor("#1a71d4 ...

  10. shell脚本学习总结01--文件描述符和重定向

    文件描述符是与文件输入和输出的相关联的整数,它们用来追踪已打开的文件,文件描述符0,1,2是系统预留的. 0 --> stdin (标准输入) 1 --> stdout (标准输出) 2 ...