Java并发编程实战

来源:互联网 发布:java支持数据库的套件 编辑:程序博客网 时间:2024/05/01 12:41

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

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

线程的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同步,可能影响活跃性和性能,所有要减小同步代码块的颗粒度。

0 0
原创粉丝点击