Java并发编程实战笔记(1)-线程安全简介

Posted by zhidaliao on January 30, 2016

多线程简介

进程出现的原因

资源利用率

程序在等待操作执行完成的空档,运行另一个程序 提高资源利用率。

公平性

用户对于计算机的使用权,通过粗粒度的时间分片 使得用户和程序共享资源程序。

便利、效率性

一个任务分解多个子任务,必要时通信共同完成任务。

异步事件的简单处理

对于单线程而言,如果要高效处理多个操作,必须使用非阻塞IO,复杂且易错,如果能够为每个请求分配线程将降低开发的难度

线程允许在同一个进程中同时存在多个程序控制流,线程会共享进程中的资源;每个线程有各自的 程序计数器、栈、局部变量。同一个进程中的多个线程也可以被同时调度到多个CPU上运行。

多线程的风险

安全性

对共享的可变的变量进行无法预测顺序的操作,将会造成与预期不一致的结果。

活跃性

某个操作无法继续进行下去:A线程占有资源长时间不释放,导致B线程无法继续操作

额外的性能开销:
  • 线程调度器,频繁的上线文切换
  • 线程共享数据/使用同步机制:抑制编译器的优化、缓存失效、共享内存总线的同步流量

线程安全性

核心在于: 避免多线程同时访问可变状态

核心知识点

  • 编写线程安全代码的核心在于:对共享 可变的状态访问操作进行管理

  • 状态可以是数据,也可以是其他依赖的对象(比如HashMap自身或者Map.Entry对象)

  • 存在多个线程访问,多个线程修改,必须使用同步

  • 线程安全类的定义: 多个线程访问某个类时,类始终表现出正确的行为

竞态条件: 于不恰当的执行顺序,而出现不正确的结果.

  • 常见类型 “先检查再执行”

  • 通过原子性原理解决 ,比如 复合操作.

  • concurrent包中包含了原子变量类.

加锁机制

  • 同步代码块将包括两个部分: 一个作为锁的对象引用;一个作为由这个锁保护的代码块

  • 正常路径退出 和 抛出异常退出,都将释放锁

  • 重入:线程获取自己持有的锁,请求会获得成功

    • 获取锁的操作粒度是 “线程”,不是”调用”

    • 一种实现方法是为每个锁关联 一个计数器和一个所有者线程.

  • 通过缩小同步代码块的范围,可以在性能和同步间找到平衡

  • 一种常见的加锁约定:通过将状态封装在对象内,通过同一个对象锁,对状态的读写访问都添加锁来确保安全

  • 用同步来协调对一个变量的访问

    • 在访问这个变量的所有位置上都需要同步

    • 所有位置上都必须使用同一个锁

    • 不仅写入需要,读取也需要同步

  • 当执行长时间计算和无法快速完成的操作,时,一定不要持有锁或者要设置定时锁(Redisson)

活跃性和性能

  • 通过缩小同步代码块的范围来保持两者平衡