java并发编程实战-第2章-线程安全性

2. 线程安全性

2.1 什么是线程安全性

线程安全类:当一个类被多个线程访问时,不管运行环境中如何调度,这些线程如何交替执行,并且在调用的代码部分不需要额为的同步或者协同。这个类为线程安全类

Thread-safe classes encapsulate any needed synchronization so that clients need not provide their own.

2.1.1. Example: A Stateless Servlet

Stateless objects are always thread-safe.

2.2 原子性

在有状态的servlet中,对状态变量count在多线程条件下,++count 这个应为原子性操作

The possibility of incorrect results in the presence of unlucky timing is so important in concurrent programming that it has a name: a race condition.

2.2.1 竞态条件

如何产生:当某个正确的结果取决于多个线程的交替执行的时序时,就会发生竞态条件

本质:通过基于一种可能失效的观察结果来做出判断或者执行某个计算

最常见类型:先检测后执行(check-then-act) (星巴克AB会见朋友例子)

2.2.2 示例:延迟初始化的竞态条件

LazyInitRace  如果应用在应用程序的注册表,可能丢失注册信息,或者对同一组注册对象表现不一致视图

@NotThreadSafe

public class LazyInitRace {

private ExpensiveObject instance = null;

public ExpensiveObject getInstance() {

if (instance == null)

instance = new ExpensiveObject();

return instance;

}

}

UnsafeSequence   如果应用在持久化的数据中,会产生不同的对象有相同的id,违反了标识的完整性约束

@NotThreadSafe

public class UnsafeSequence {

private int value;

/** Returns a unique value. */

public int getNext() {

return value++;

}

}

2.2.3 复合操作

像++count,这种“读取-修改-写入”的操作统称为复合操作,复合操作应该是原子性的。

1、通过2.3的加锁机制

2、使用现有线程安全类AtomicLong

Listing 2.4. Servlet that Counts Requests Using AtomicLong.

@ThreadSafe

public class CountingFactorizer implements Servlet {

private final AtomicLong count = new AtomicLong(0);

public long getCount() { return count.get(); }

public void service(ServletRequest req, ServletResponse resp) {

BigInteger i = extractFromRequest(req);

BigInteger[] factors = factor(i);

count.incrementAndGet();

encodeIntoResponse(resp, factors);

}

}

Where practical, use existing thread-safe objects, like AtomicLong, to manage your class‘s state. It is simpler to reason about the possible states and state transitions for existing thread-safe objects than it is for arbitrary state variables, and this makes it easier to maintain and verify thread safety.

实际中,尽可能使用现有的线程安全类来管理类的状态

2.3 加锁机制

例子:UnsafeCachingFactorizer

To preserve state consistency, update related state variables in a single atomic operation

为了保持状态的一致性,则需在一个原子操作中更新所有的相关的状态变量

2.3.1 内置锁

java提供内置锁机制支持原子性:the synchronized block

these built-in locks are called intrinsic locks or monitor locks

2.3.2 重入

某个线程可以获得已经持有的锁

2.4 用锁来保护状态

对于所有可变的状态,都需要使用同一个锁来保护

如果所有方法都同步,会造成活跃性问题和性能问题

2.5 活跃性和性能

network or console I/O. 不要持有锁

3.对象的共享

时间: 2024-11-03 05:30:46

java并发编程实战-第2章-线程安全性的相关文章

《Java并发编程实战》第二章 线程安全性 读书笔记

一.什么是线程安全性 编写线程安全的代码 核心在于要对状态访问操作进行管理. 共享,可变的状态的访问 - 前者表示多个线程访问, 后者声明周期内发生改变. 线程安全性 核心概念是正确性.某个类的行为与其规范完全一致. 多个线程同时操作共享的变量,造成线程安全性问题. * 编写线程安全性代码的三种方法: 不在线程之间共享该状态变量 将状态变量修改为不可变的变量 在访问状态变量时使用同步 Java同步机制工具: synchronized volatile类型变量 显示锁(Explicit Lock

读书笔记-----Java并发编程实战(一)线程安全性

线程安全类:在线程安全类中封装了必要的同步机制,客户端无须进一步采取同步措施 示例:一个无状态的Servlet 1 @ThreadSafe 2 public class StatelessFactorizer implements Servlet{ 3 public void service(ServletRequest req,ServletResponse resp){ 4 BigInteger i = extractFromRequest(req); 5 BigInteger[] fact

《Java并发编程实战》第八章 线程池的使用 读书笔记

一.在任务与执行策略之间的隐性解耦 有些类型的任务需要明确地指定执行策略,包括: . 依赖性任务.依赖关系对执行策略造成约束,需要注意活跃性问题.要求线程池足够大,确保任务都能放入. . 使用线程封闭机制的任务.需要串行执行. . 对响应时间敏感的任务. . 使用ThreadLocal的任务. 1. 线程饥饿死锁 线程池中如果所有正在执行任务的线程都由于等待其他仍处于工作队列中的任务而阻塞,这种现象称为线程饥饿死锁. 2. 运行时间较长的任务 Java提供了限时版本与无限时版本.例如Thread

Java并发编程实战 第16章 Java内存模型

什么是内存模型 JMM(Java内存模型)规定了JVM必须遵循一组最小保证,这组保证规定了对变量的写入操作在何时将对其他线程可见. JMM为程序中所有的操作定义了一个偏序关系,称为Happens-Before.两个操作缺乏Happens-Before关系,则Jvm会对它们进行任意的重排序. Happends-Before的规则包括: 1. 程序顺序规则.若程序中操作A在操作B之前,则线程中操作A在操作B之前执行. 2. 监视器锁规则.在同一监视器锁上的解锁操作必须在加锁操作之前执行.如图所示,

JAVA并发编程实战---第三章:对象的共享(2)

线程封闭 如果仅仅在单线程内访问数据,就不需要同步,这种技术被称为线程封闭,它是实现线程安全性的最简单的方式之一.当某个对象封闭在一个线程中时,这种方法将自动实现线程安全性,即使被封闭的对象本生不是线程安全的. 实现好的并发是一件困难的事情,所以很多时候我们都想躲避并发.避免并发最简单的方法就是线程封闭.什么是线程封闭呢? 就是把对象封装到一个线程里,只有这一个线程能看到此对象.那么这个对象就算不是线程安全的也不会出现任何安全问题.实现线程封闭有哪些方法呢? 1:ad-hoc线程封闭 这是完全靠

《Java并发编程实战》第九章 图形用户界面应用程序界面 读书笔记

一.为什么GUI是单线程化 传统的GUI应用程序通常都是单线程的. 1. 在代码的各个位置都须要调用poll方法来获得输入事件(这样的方式将给代码带来极大的混乱) 2. 通过一个"主事件循环(Main Event Loop)"来间接地运行应用程序的全部代码. 假设在主事件循环中调用的代码须要非常长时间才干运行完毕,那么用户界面就会"冻结",直到代码运行完毕.这是由于仅仅有当运行控制权返回到主事件循环后,才干处理兴许的用户界面事件. 非常多尝试多线程的GUI框架的努力

Java并发编程实战 第15章 原子变量和非阻塞同步机制

非阻塞的同步机制 简单的说,那就是又要实现同步,又不使用锁. 与基于锁的方案相比,非阻塞算法的实现要麻烦的多,但是它的可伸缩性和活跃性上拥有巨大的优势. 实现非阻塞算法的常见方法就是使用volatile语义和原子变量. 硬件对并发的支持 原子变量的产生主要是处理器的支持,最重要的是大多数处理器架构都支持的CAS(比较并交换)指令. 模拟实现AtomicInteger的++操作 首先我们模拟处理器的CAS语法,之所以说模拟,是因为CAS在处理器中是原子操作直接支持的.不需要加锁. public s

JAVA并发编程实战---第三章:对象的共享

在没有同步的情况下,编译器.处理器以及运行时等都可能对操作的执行顺序进行一些意想不到的调整.在缺乏足够同步的多线程程序中,要对内存操作的执行顺序进行判断几乎无法得到正确的结果. 非原子的64位操作 当线程在没有同步的情况下读取变量时,可能会读到一个失效值,但至少这个值是由之前的某个线程设置,而不是一个随机值.这种安全性保证也被称为最低安全性. Java内存模型要求:变量的读取操作和写入操作都必须是原子操作,但对于非Volatile类型的long和Double变量,JVM允许将64的读操作或写操作

Java并发编程(十二)线程安全性的委托

在组合对象中如果每个组件都已经是线程安全的,是否需要再加一个额外的"线程安全层",需要视情况而定. final可以修饰未复制的属性,只要在静态代码块或者构造函数中赋值了即可. 独立的状态变量 我们还可以将线程的安全性委托给多个状态变量,只要这些变量是彼此独立的,即组合而成的类并不会在其包含的多个状态变量上增加任何不变性条件. 如果是状态变量之间存在着某些不变性条件时就没这么简单了.比如一个类中有两个属性,要满足的条件是一个属性的值要小于另一个属性的值.这是比较典型的先检查后执行的操作,