标签 ‘ JAVA

聊聊并发-Java中的Copy-On-Write容器

Copy-On-Write简称COW,是一种用于程序设计中的优化策略。其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略。从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。CopyOnWrite容器非常有用,可以在非常多的并发场景中使用到。

什么是CopyOnWrite容器

CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

阅读全文

无锁和无等待的定义和例子

原文链接译文连接,译者:周可人,校对:梁海舰

在查阅google之后,我发现没有一处对并发算法或是数据结构规定的演进条件(progress condition,注:参考[1],译者认为翻译为演进状态更为合适)做合理的解释。甚至在”The Art of Multiprocessor Programming“中也只有围绕书本的一小段定义,大部分定义是单行的句子,因而造成了我们普通人含义模糊的理解,所以在这里我把我对这些概念的理解整理在一起,并且在每一种概念后面给出相应的例子。

我们先将演进条件分为四个主要类别,阻塞(blocking),无干扰(obstruction-free),无锁(lock-free),和无等待(wait-free)。详细列表如下:
阅读全文

《Java 并发编程的艺术》迷你书

本文源自InfoQ发表的《Java 并发编程的艺术》电子书  作者:方腾飞  序言:张龙 免费下载此迷你书

250

推荐序

欣闻腾飞兄弟的《聊聊并发》系列文章将要集结成InfoQ迷你书进行发布,我感到非常的振奋。这一系列文章从最开始的发布到现在已经经历了两年多的时间,这两年间,Java世界发生了翻天覆地的变化。Java 7已经发布,而且Java 8也将在下个月姗姗来迟。围绕着JVM已经形成了一个庞大且繁荣的生态圈,Groovy、Scala、Clojure、Ceylon等众多JVM语言在蓬勃发展着,如今的Java已经不是几年前的Java了,众多运行在JVM上的编程语言为我们带来了更多的选择,提供了更好的机会。

纵观这几年的技术发展趋势,唱衰Java的论调一直都萦绕在我们耳边。不可否认,Java的发展确实有些缓慢,而且有些臃肿;但放眼望去,有如此之多的核心与关键系统依旧在使用Java进行开发并运行在JVM之上,这不仅得益于Java语言本身,强大的JVM及繁荣的Java生态圈在这其中更是发挥着重要的作用。在Java的世界中,我们想要完成一件事情有太多可用的选择了。

虽然如此,对于国内的一些开发人员来说,但凡提到Java,想到的都是所谓的SSH(Struts、Spring及Hibernate等相关框架)。不可否认,这些框架对于我们又快又好地完成任务起到了至关重要的推进作用,然而Java并不是SSH,SSH也不是Java的代名词。 阅读全文

Oracle官方并发教程之执行器(Executors)

原文链接译文链接,译者:Greenster,校对:郑旭东

在之前所有的例子中,Thread对象表示的线程和Runnable对象表示的线程所执行的任务之间是紧耦合的。这对于小型应用程序来说没问题,但对于大规模并发应用来说,合理的做法是将线程的创建与管理和程序的其他部分分离开。封装这些功能的对象就是执行器,接下来的部分将讲详细描述执行器。
阅读全文

Oracle官方并发教程之线程池

原文链接译文链接,译者:Greenster,校对:郑旭东

在java.util.concurrent包中多数的执行器实现都使用了由工作线程组成的线程池,工作线程独立于所它所执行的Runnable任务和Callable任务,并且常用来执行多个任务。

使用工作线程可以使创建线程的开销最小化。在大规模并发应用中,创建大量的Thread对象会占用占用大量系统内存,分配和回收这些对象会产生很大的开销。

阅读全文

Oracle官方并发教程之Executor接口

原文链接译文链接,译者:Greenster,校对:郑旭东

java.util.concurrent中包括三个Executor接口:

  • Executor,一个运行新任务的简单接口。
  • ExecutorService,扩展了Executor接口。添加了一些用来管理执行器生命周期和任务生命周期的方法。
  • ScheduledExecutorService,扩展了ExecutorService。支持Future和定期执行任务。

通常来说,指向Executor对象的变量应被声明为以上三种接口之一,而不是具体的实现类。 阅读全文

怎么理解Condition

版权声明:本文为本作者原创文章,转载请注明出处。感谢码梦为生| 刘锟洋 的投稿。

在java.util.concurrent包中,有两个很特殊的工具类,Condition和ReentrantLock,使用过的人都知道,ReentrantLock(重入锁)是jdk的concurrent包提供的一种独占锁的实现。它继承自Dong Lea的 AbstractQueuedSynchronizer(同步器),确切的说是ReentrantLock的一个内部类继承了AbstractQueuedSynchronizer,ReentrantLock只不过是代理了该类的一些方法,可能有人会问为什么要使用内部类在包装一层? 我想是安全的关系,因为AbstractQueuedSynchronizer中有很多方法,还实现了共享锁,Condition(稍候再细说)等功能,如果直接使ReentrantLock继承它,则很容易出现AbstractQueuedSynchronizer中的API被误用的情况。
阅读全文

Java 7中的TransferQueue

原文链接译文链接,作者:Alex Miller,译者:Greenster,校对:梁海舰

Java7中加入了JSR 166y规范对集合类和并发类库的改进。其中的一项是增加了接口TransferQueue和其实现类LinkedTransferQueue

TransferQueue继承了BlockingQueueBlockingQueue又继承了Queue)并扩展了一些新方法。BlockingQueue(和Queue)是Java 5中加入的接口,它是指这样的一个队列:当生产者向队列添加元素但队列已满时,生产者会被阻塞;当消费者从队列移除元素但队列为空时,消费者会被阻塞。
阅读全文

Initialization On Demand Holder idiom的实现探讨

SingletonLogoTransparent

起源

程晓明同学的文章“双重检查锁定与延迟初始化”中,提到了对于单例模式的“Initialization On Demand Holder idiom”实现方案。

这个方案的技术实质是利用Java类初始化的LC锁。相比其他实现方案(如double-checked locking等),该技术方案的实现代码较为简洁,并且在所有版本的编译器中都是可行的。该方案代码如下:
阅读全文

Oracle官方并发教程之中断

原文链接译文链接译者: 蘑菇街-小宝   校对:梁海舰
中断是给线程的一个指示,告诉它应该停止正在做的事并去做其他事情。一个线程究竟要怎么响应中断请求取决于程序员,不过让其终止是很普遍的做法。这是本文重点强调的用法。
阅读全文

招募译者翻译并发数据结构

什么是并发数据结构? 引用wiki上的定义

In computer science, a concurrent data structure is a particular way of storing and organizing data for access by multiple computing threads (or processes) on a computer.

简而言之,并发数据结构即允许多线程同时访问(读和写)的数据结构。

阅读全文

Oracle官方并发教程之不可变对象

原文链接译文链接,译者:Greenster,校对:郑旭东

一个对象如果在创建后不能被修改,那么就称为不可变对象。在并发编程中,一种被普遍认可的原则就是:尽可能的使用不可变对象来创建简单、可靠的代码。
阅读全文

Oracle官方并发教程之Guarded Blocks

原文连接译文连接,译者:Greester,校对:郑旭东

多线程之间经常需要协同工作,最常见的方式是使用Guarded Blocks,它循环检查一个条件(通常初始值为true),直到条件发生变化才跳出循环继续执行。在使用Guarded Blocks时有以下几个步骤需要注意:

假设guardedJoy()方法必须要等待另一线程为共享变量joy设值才能继续执行。那么理论上可以用一个简单的条件循环来实现,但在等待过程中guardedJoy方法不停的检查循环条件实际上是一种资源浪费。
阅读全文

happens-before俗解

学习Java并发,到后面总会接触到happens-before偏序关系。初接触玩意儿简直就是不知所云,下面是经过一段时间折腾后个人对此的一点浅薄理解,希望对初接触的人有帮助。如有不正确之处,欢迎指正。

synchronized、大部分锁,众所周知的一个功能就是使多个线程互斥/串行的(共享锁允许多个线程同时访问,如读锁)访问临界区,但他们的第二个功能 —— 保证变量的可见性 —— 常被遗忘。

为什么存在可见性问题?简单介绍下。相对于内存,CPU的速度是极高的,如果CPU需要存取数据时都直接与内存打交道,在存取过程中,CPU将一直空闲,这是一种极大的浪费,妈妈说,浪费是不好的,所以,现代的CPU里都有很多寄存器,多级cache,他们比内存的存取速度高多了。某个线程执行时,内存中的一份数据,会存在于该线程的工作存储中(working memory,是cache和寄存器的一个抽象,这个解释源于《Concurrent Programming in Java: Design Principles and Patterns, Second Edition》§2.2.7,原文:Every thread is defined to have a working memory (an abstraction of caches and registers) in which to store values. 有不少人觉得working memory是内存的某个部分,这可能是有些译作将working memory译为工作内存的缘故,为避免混淆,这里称其为工作存储,每个线程都有自己的工作存储),并在某个特定时候回写到内存。单线程时,这没有问题,如果是多线程要同时访问同一个变量呢?内存中一个变量会存在于多个工作存储中,线程1修改了变量a的值什么时候对线程2可见?此外,编译器或运行时为了效率可以在允许的时候对指令进行重排序,重排序后的执行顺序就与代码不一致了,这样线程2读取某个变量的时候线程1可能还没有进行写入操作呢,虽然代码顺序上写操作是在前面的。这就是可见性问题的由来。

阅读全文

AbstractQueuedSynchronizer的介绍和原理分析

感谢同事【魏鹏】投递本稿。 Dedicate to Molly.

简介

提供了一个基于FIFO队列,可以用于构建锁或者其他相关同步装置的基础框架。该同步器(以下简称同步器)利用了一个int来表示状态,期望它能够成为实现大部分同步需求的基础。使用的方法是继承,子类通过继承同步器并需要实现它的方法来管理其状态,管理的方式就是通过类似acquire和release的方式来操纵状态。然而多线程环境中对状态的操纵必须确保原子性,因此子类对于状态的把握,需要使用这个同步器提供的以下三个方法对状态进行操作:

  • java.util.concurrent.locks.AbstractQueuedSynchronizer.getState()
  • java.util.concurrent.locks.AbstractQueuedSynchronizer.setState(int)
  • java.util.concurrent.locks.AbstractQueuedSynchronizer.compareAndSetState(int, int)

子类推荐被定义为自定义同步装置的内部类,同步器自身没有实现任何同步接口,它仅仅是定义了若干acquire之类的方法来供使用。该同步器即可以作为排他模式也可以作为共享模式,当它被定义为一个排他模式时,其他线程对其的获取就被阻止,而共享模式对于多个线程获取都可以成功。

阅读全文

return top