`

java并发-1

阅读更多

Java 并发核心编程

内容涉及:

1、关于java并发

2、概念

3、保护共享数据

4、并发集合类

5线程

6、线程协作及其他

 

1、关于java并发

自从java创建以来就已经支持并发的理念,如线程和锁。这篇指南主要是为帮助java多线程开发人员理解并发的核心概念以及如何应用这些理念。本文的主题是关于具有java语言风格Threadsynchronizedvolatile以及J2SE5中新增的概念,如锁(Lock)、原子性(Atomics)、并发集合类、线程协作摘要Executors。开发者通过这些基础的接口可以构建高并发、线程安全的java应用程序。

 

2、概念

本部分描述java并发概念在这篇DZone Refard会被通篇使用。

JVM并发看CPU内存指令重排序(Memory Reordering):http://kenwublog.com/illustrate-memory-reordering-in-cpu

java内存模型详解http://kenwublog.com/explain-java-memory-model-in-detail

概念

描述

Java Memory Model

Java内存模型

JavaSE5JSR133)中定义的Java Memory ModelJMM)是为了确保当编写并发代码的时候能够提供Java程序员一个可用的JVM实现。术语JMM的作用类似与一个观察同步读写字段的monitor。它按照“happens-before order(先行发生排序)”的顺序—可以解释为什么一个线程可以获得其他线程的结果,这组成了一个属性同步的程序,使字段具有不变性,以及其他属性。

monitor

Monitor

Java语言中,每个对象都拥有一个访问代码关键部分并防止其他对象访问这段代码的“monitor”(每个对象都拥有一个对代码关键部分提供访问互斥功能的“monitor”)。这段关键部分是使用synchronized对方法或者代码标注实现的。同一时间在同一个monitor中,只允许一个线程运行代码的任意关键部分。当一个线程试图获取代码的关键部分时,如果这段代码的monitor被其他线程拥有,那么这个线程会无限期的等待这个monitor直到它被其他线程释放。除了访问互斥之外,monitor还可以通过waitnotify来实现协作。

原子字段赋值

Atomic field assignment

除了doubleslangs之外的类型,给一个这些类型的字段赋值是一个原子操作。在JVM中,doubleslangs的更新是被实现为2个独立的操作,因此理论上可能会有其他的线程得到一个部分更新的结果。为了保护共享的doubleslangs,可以使用volatile标记这个字段或者在synchronized修饰的代码块中操作字段。

竞争状态

Race condition

竞争发生当不少于一个线程对一个共享的资源进行一系列的操作,如果这些线程的操作的顺序不同,会导致多种可能的结果。

数据竞争

Data race

数据竞争主要发生在多个线程访问一个共享的、non-finalnon-volatile、没有合适的synchronization限制的字段。Java内存模型不会对这种非同步的数据访问提供任何的保证。在不同的架构和机器中数据竞争会导致不可预测的行为。

安全发布

Safe publications

在一个对象创建完成之前就发布它的引用时非常危险的。避免这种使用这种引用的一种方法就是在创建期间注册一个回调接口。另外一种不安全的情况就是在构造子中启动一个线程。在这2种情况中,非完全创建的对象对于其他线程来说都是可见的。

不可变字段

Final Fields

不可变字段在对象创建之后必须明确设定一个值,否则编译器就会报出一个错误。一旦设定值后,不可变字段的值就不可以再次改变。将一个对象的引用设定为不可变字段并不能阻止这个对象的改变。例如,ArrayList类型的不可变字段不能改变为其他ArrayList实例的引用,但是可以在这个list实例中添加或者删除对象。

    在创建结尾,对象会遇到final field freeze:如果对象被安全的发布后,即使在没有synchronization关键字修饰的情况下,也能保证所有的线程获取final字段在构建过程中设定的值。final field freezer不仅对final字段有用,而且作用于final对象中的可访问属性。

不可变对象

Immutable objects

在语法上final 字段能够创建不需要synchronization修饰的、能够被共享读取的线程安全的不可变对象。实现Immutable Object需要保证如下条件:

·对象被安全的发布(在创建过程中this 引用是无法避免的)

·所有字段被声明为final

·在创建之后,在对象字段能够被访问的范围中是不允许修改这个字段的。

·class被声明为final(为了防止subclass违反这些规则)

 

 

3、保护共享数据

编写线程安全的java程序,当修改共享数据的时候要求开发人员使用合适的锁来保护数据。锁能够建立符合Java Memory Model要求的访问顺序,而且确保其他线程知道数据的变化。

注意:

Java Memory Model,如果没有被synchronization修饰,改变数据不需要什么特别的语法表示JVM能够自由地重置指令顺序的特性和对可见性的限制方式很容易让开发人员感到奇怪。

 

3.1Synchronized

每个对象实例都拥有一个每次只能让一个线程锁住的monitorsynchronized能够用在一个方法或者代码块中来锁住这个monitor。用synchronized修饰一个对象,当修改这个对象的一个字段,synchronized保证其他线程余下的对这个对象的读操作能够获取修改后的值。需要注意的是修改同步块之外的数据或者synchronized没有修饰当前被修改的对象,那么不能保证其他线程读到这些最新的数据synchronized关键字能够修饰一个对象实例中的函数或者代码块。在一个非静态方法中this关键字表示当前的实例对象。在一个synchronized修饰的静态的方法中,这个方法所在的类使用Class作为实例对象。

 

3.2Lock

Java.util.concurrent.locks包中有个标准Lock接口。ReentrantLock 实现了Lock接口,它完全拥有synchronized的特性,同时还提供了新的功能:获取Lock的状态、非阻塞获取锁的方法tryLock()、可中断Lock

下面是使用ReentrantLock的详细示例:

public class Counter{

private final Lock lock = new ReentrantLock();

private int value;

public int increment() {

lock.lock();

try {

return ++value;

}finally{

lock.unlock();

}

}

}

 

 

3.3ReadWriteLock

Java.util.concurrent.locks包中还有个ReadWriteLock接口(实现类是ReentrantWriteReadLock),它定义一对锁:读锁和写锁,特征是能够被并发的读取但每次只能有一个写操作。使用ReentrantReadWriteLock并发读取特性的详细示例:

public class ReadWrite {

private final ReadWriteLock lock = new ReentrantReadWriteLock();

private int value;

public void increment(){

lock.writeLock().lock();

try{

value++;

}finally{

lock.writeLock().lock();

}

}

public int current(){

lock.readLock().lock();

try{

return value;

}finally{

lock.readLock().unlock();

}

}

}

 

3.4volatile

volatile原理与技巧http://kenwublog.com/the-theory-of-volatile

volatile修饰符用来标注一个字段,表明任何对这个字段的修改都必须能被其他随后访问的线程获取到,这个修饰符和同步无关。因此,volatile修饰的数据的可见性和synchronization类似,但是这个它只作用于对字段的读或写操作。在JavaSE5之前,因为JVM的架构和实现的原因,不同JVMvolatile效果是不同的而且也是不可信的。下面是Java内存模型明确地定义volatile的行为:

public class Processor implements Runnable {

private volatile boolean stop;

public void stopProcessing(){

stop = true;

}

public void run() {

while (!stop) {

//do processing

}

}

}

注意:使用volatile修饰一个数组并不能让这个数组的每个元素拥有volatile特性,这种声明只是让这个数组的reference具有volatile属性。数组被声明为AtomicIntegerArray类型,则能够拥有类似volatile的特性。

3.5、原子类

使用volatile的一个缺点是它能够保证数据的可见性,却不能在一个原子操作中对volatile修饰的字段同时进行校验和更新操作。java.util.concurrent.atomic包中有一系列支持在单个非锁定(lock)的变量上进行原子操作的类,类似于volatile。示例:

public class Counter{

private AtomicInteger value = new AtomicInteger();

private int value;

public int increment() {

return value.incrementAndGet();

}

}

incrementAndGet方法是原子类的复合操作的一个示例。booleans, integers, longs, object references, integers数组, longs数组, object references数组 都有相应的原子类。

 

3.6ThreadLocal

通过ThreadLocal能数据保存在一个线程中,而且不需要lock同步。理论上ThreadLocal可以让一个变量在每个线程都有一个副本。ThreadLocal常用来屏蔽线程的私有变量,例如“并发事务”或者其他的资源。而且,它还被用来维护每个线程的计数器,统计,或者ID生成器。

public class TransactionManager {

private static final ThreadLocal<Transaction> currentTransaction 

= new ThreadLocal<Transaction>() {

@Override

protected Transaction initialValue() {

return new NullTransaction();

}

};

public Transaction currentTransaction() {

font-size: 13pt; color: #f0dfaf; font-family

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics