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

本文详细阐述了进程与线程的概念及其在操作系统中的作用,并深入探讨了多线程的优势与并发编程中的常见问题,如线程安全性、活跃性和性能问题等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

进程与线程
操作系统可以使计算机同时能运行多个程序,每个不同的程序在单独的进程中运行。
同一个进程可以有多个线程的程序控制流。线程可以共享进程范围内的资源,但每个线程有各自的计数器,栈和局部变量。

多线程可以提升资源的利用率及系统吞吐量。尤其是多处理器的环境,可以提高处理器资源的利用率。

线程的6种状态:

  • New(新创建)
  • Runnable(可运行)
  • Blocked(阻塞)
  • Waiting(等待)
  • Timed Waiting(计时等待)
  • Terminated( 终止)

多线程带来并发问题:

  • 安全性问题 (共享对象的可变状态的正确性和一致性)
  • 活跃性问题 (死锁,饥饿,活锁,丢失信号)
  • 性能问题 (多个线程的上下文切换,线程创建与调度)

所有并发问题都是源于多线程访问共享对象的可变状态。

线程安全性:
核心是对对象状态访问操作的管理,特别是对共享的和可变的状态的访问。

什么是线程安全的类?
当多个线程访问某个类的对象时,主调代码不要额外的同步或协调,这个对象都表现出正确的行为,这个类是线程安全的。

哪些类是线程安全的类?

  • 无状态
  • 状态不可修改(所有的域是final, 只通过构造器初始化,对象域不逸出)
  • 在访问可变状态变量时使用同步(原子操作)

Java的同步机制,包括内置锁synchronized, volatile变量,原子变量,显式锁。

线程安全性核心是正确性

正确性:包括 各种不变性条件(Invariant)来约束对象状态,以及后验条件(Postcondition)来描述对象操作结果。

//不变性条件是 end datestart date 之后
public class Range {
    private Date start;
    private Date end;

    public Range(Date start, Date end) {
        this.start = start;
        this.end = end;
    }
}

在并发编程中,当某个计算的正确性取决于多个线程的执行时序是,称为竞争条件(Race Condition)。常见的竞争条件类型是“先检查后执行”。
示例1:延时初始化的race condition

public class CustomerService {
    private Map<String, Customer> customers = new HashMap<>();
    public Customer getCustomer(String name) {
        if(customers.get(name) == null) {//race condition
            customers.put(name, new Customer(name));
        } 
        return customers.get(name);
    }
}

示例2:复合操作的race condition
计数器i++, 包括“读取-修改-写入”

public class Counting {
    private int i;
    public void increase() {
        i++; //race condition
    }
    public int getCount() {
        return i;
    }
}

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

上面的示例可以使用加锁和原子类型变量来实现原子操作:

public class CustomerService {
    private Map<String, Customer> customers = new HashMap<>();
    public synchronized Customer getCustomer(String name) { //加内置锁
        if(customers.get(name) == null) {//race condition
            customers.put(name, new Customer(name));
        } 
        return customers.get(name);
    }
}
public class Counter {
    private final AtomicLong count = new AtomicLong();//原子类型类
    public void increase() {
        count.incrementAndGet();
    }
    public long getCount() {
        return count.get();
    }
}

加锁机制
Java的内置锁机制保证操作的原子性。
对可能被多个线程同时访问的可变状态变量,需要加锁保护。
对每个包含多个变量的不变性条件,涉及的所有变量都需要加同一个锁保护。

静态synchronized方法的锁是Class对象锁,和非静态synchronized方法的不是同一个锁。

如果使用synchronized同步,可能影响活跃性和性能,所有要减小同步代码块的颗粒度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值