Java Concurrency in Practice ---线程安全性

本文探讨了线程安全的概念,指出无状态对象如Servlet是线程安全的。详细介绍了原子性、竞态条件和复合操作,强调了加锁机制在确保线程安全中的作用。通过示例展示了如何使用同步代码块和synchronized关键字来保护状态,避免竞态条件。
摘要由CSDN通过智能技术生成

1. 什么是线程安全性

当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么就称这个类是线程安全的。再解释一下就是:

当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。

无状态对象一定是线程安全的,而大多数 Servlet 都是无状态的,从而极大地降低了在实现 Servlet 线程安全时的复杂性。只有当 Servlet 在处理请求时需要保存一些信息,线程安全才会成为一个问题。


2. 原子性

在多线程中进行count++的操作是很不安全的。这看上去仅仅是一个操作,但这个操作并非原子的,因而它并不会作为一个不可分割的操作来执行。

实际上,它可以分解为三个独立的操作:读取 count 的值,将其值加 1,再将结果写入 count。即:读取-->修改-->写入的操作序列,使得结果状态依赖前面的状态。

因此,如果在两个线程没有同步的情况下,发生了如下状态:

这里写图片描述

也就是如果多个线程的操作交替执行,那么会导致这两个线程读取到的值相同的,同时执行加 1 操作,得到的结果也就是相同的。而这不是我们希望的结果。

原子性也可以被解释为不可分割的操作。


2.1 竞态条件

当某个计算的正确性取决于多个线程的交替执行时序时,就会发生竞态条件。也就是说正确的结果需要靠运气。因此,竞态条件并不总是会产生错误,而是在某种不恰当的执行时序情况下会。最常见的竞态条件是“先检查后执行”操作,即通过一个可能失效的观测结果来决定下一步动作。

举个例子:延迟初始化中的竞态条件

延迟初始化的目的是将对象的初始化操作推迟到实际被使用是才进行,同时要确保只被初始化一次。

@NotThreadSafe
public class LazyInitRace{
   
    private ExpensiveObject instance = null;

    public ExpensiveOvject getInstance(){
        if (instance == null)
            instance = new ExpensiveObject();
        return instance;
    } 
}

上面的 LazyInitRace 类说明了这个问题。首先 getInstance 方法将判断 ExpensiveObj

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值