Java并发编程与锁
2018-10-03 16:46:21 4 举报
AI智能生成
Java并发编程与锁
作者其他创作
大纲/内容
锁
Lock接口
特点:显示声明调用,虽然不便捷。但1)尝试非阻塞地获取锁 2)能被中断地获取锁 3)超时获取锁
抽象类 队列同步器AQS
实现
前提变量
资源获取线程的排队工作
内置FIFO队列
用于存放 等待中的线程
队列头结点才能拥有同步状态。 其他节点都是自旋
同步状态
成员变量 int state
用数量表示加锁的次数。
修改的原子性:通过CAS保证
基础方法(用于访问修改同步状态))
getState()、setState(int newState) 、compareAndSetState(int expect , int update)
模板方法
独占式 获取与释放同步状态(另有 相应中断。 超时限制)
共享式 获取与释放同步状态(另有 相应中断。 超时限制)
获取等待在 同步队列 上的 线程集合情况
自定义锁 并使用
使用静态内部类 继承AQS
根据所需锁类型,重写需要的方法。独占or共享,是否相应中断,是否超时限制
队列同步器
只有前驱节点是 头结点 才尝试获取 同步状态,获取释放之后,将唤醒其后缀节点,并开始获取 同步状态
同步状态获取与释放
独占式
1)先尝试获取同步状态 2)失败则生成节点,加入同步队列尾部 3) 若前驱是头结点,则会再次尝试获取同步状态 不是前驱头结点,则会进入等待状态。直到被中断,或者前驱节点唤醒他。 4)获取失败则重新加入 同步队列
共享式
区别:因为多个线程能获取到同步状态,所以在释放同步状态 必须同步,安全释放,通过循环和CAS
重入锁
线程可重复进入自己持有的锁,要注意锁的最终释放。
实现原理
线程变量
锁具有一个线程变量,当加锁后,锁的线程变量置为当前持有锁的变量
每重入一次,state+1,在解锁时,前n-1次解锁都是false,最后一次置线程变量为null
公平锁
等待时间最长的线程,有限获得锁
实现原理
前缀节点
获取锁时,要判断同步队列当前节点,是否有前驱节点。有前驱则不获取锁。
读写锁
读锁是共享锁,写锁是排他锁。都可重入(本线程可重复进入)
实现原理
32位整型变量S,前16位表示读锁,后16位表示写锁。
写入锁状态
写入写状态直接+1.读状态S+(1<<16).
获取锁状态
写状态:S & 0000FFFF 将高位16位全部抹去。 读状态:S>>>16
锁降级
写锁降级为读锁
不是解写锁,再锁读锁。而是先锁读锁,再解写锁。(至少保证存在一个锁)
必要性
保证数据的可见性
反例
A释放写锁。B持有写锁,修改数据,对A不可见。
应用锁降级
A修改完,锁读锁,放写锁。B发现A还持有读锁,B阻塞在写锁上。
ReentrantLock
实现
AQS
volatile变量
整型变量(命名为state)来维护同步状态
CAS
final native本地方法
unsafe.cpp
atomic.cpp
atomic_windows_x86.inline.hpp
多处理器,则为cmpxchg指令加上Lock前缀(总线锁or缓存锁)保证顺序一致性。(总线锁or缓存锁)。单处理器不加
子主题
Java并发编程
线程
线程优先级
分配原则:频繁阻塞的线程(高优先级),偏重计算(低优先级,因为如果高的话 计算就一直占用CPU不放开了)
Daemon线程
支持性工作,只剩下守护线程JVM会退出。只能在线程启动前,设置为守护线程。其不能依靠finally释放资源
状态
阻塞
线程阻塞于进入synchronized的状态
等待
wait(),join(),或者线程因Lock接口的阻塞(因为实现了LockSupport类中的方法)
超时等待
sleep,wait。LockSupport。超时会返回运行状态
运行状态
包括运行与就绪,区别在于是否获得了CPU时间片
流程
构造线程
起个名字方便Jstack监控。线程对象属性:由父进程在堆中分配内存,集成父进程Deamon,优先级,加载资源的contextClassLoader,分配一个线程ID
启动线程
初始化后,父进程通知JVM,只要 线程规划器空间,立即启动线程。调用start()
中断线程
A中断B,其实是A发了一个消息给B,让B中断自己。
终止线程
废弃的suspend(),resume(),stop()。因为不会释放已经占有的资源(比如锁),带着资源进入睡眠。容易死锁。
采用cancel(), 通过修改一个标志位,让运作中的线程自行发现需要取消。让其释放完所有资源后,主动终止自身。
线程间通信
volatile和synchronized关键字
等待/通知机制
依托于同步机制,目的是确保等待线程从wait()方法返回时 ,能感知线程对变量的修改。
经典范式
等待方
1)获取Object对象的锁 2)检查条件,若获取不满足,则调用Object的wait()方法,被notify()后仍要检查条件,是否能获取锁。
通知方
1)获取Object对象的锁 2)改变条件,notifyAll所有等待在Object对象上的线程
管道输入/输出流
pipe字符流,字节流。输入输出,使用时需要绑定。out.connect(in),否则会抛异常
线程变量ThreadLocal
附带在线程上,一个线程可以根据ThreadLocal查询到绑定在其上的一个值。通过set和get。
常见用法: 耗时统计,set一个时间,get当前时间减去ThreadLocal就是耗时
线程应用实例
等待超时的实现
变量准备
等待持续时间(输入参数): REMAINING = T
超时时间: FUTURE = now + T
while( pool.isEmpty && remain>0){ wait(remain); remaining = future - now;} 也就是说,在future到来以前,这个线程可以被notify唤醒,去判断资源。要么取到,要么继续wait剩下的时间。
简单的数据库连接池
未完成,待完善
线程池技术
好处
1)客户端传入大量短小的任务,消除频繁新建,切换,销毁线程的系统资源开销。 2)面对过量任务,能平缓地处理。
细节点
包含参数:最大连接数,默认连接数,最小连接数。工作列表jobs,工作者列表works。
但凡要操作包含jobs,都要进行同步。
在主动中断或者移除线程池工作线程时,通过改变工作线程标记位,良性中断线程。
收藏
收藏
0 条评论
下一页