并发编程的艺术

并发编程的挑战

上下文切换

死锁

java并发底层原理

volatile

1
2
3
修改变量后,其他线程里面可见

可见,有序

synchronized

1
2
3
4
5
6
对象头
无锁 hash, 年龄,是否偏向
偏向锁 线程id,epoch,年龄,是否偏向
轻量级锁 线程id
重量级锁 线程id
gc 空

锁定升级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
偏向锁(可关闭)
添加
1,锁记录当前线程
2,使用cas竞争锁
3,成功
撤销
1,有竞争才会尝试释放锁
2,撤销,只有等待安全点(没有正在执行的字节码)
3,暂停持有偏向锁线程,不活动->无锁
活动-->偏向其他线程,无锁,标记对象不适合作为偏向锁
轻量级锁
添加
1,当前栈桢创建存储锁记录的空间,并复制mark word到锁记录(displaced mark word)
2,尝试cas将对象头中的记录替换为锁记录的指针
3,成功,则获取锁.失败,则有竞争,尝试使用自旋获取锁.
撤销
1,cas操作dispalaced mark word替换到对象头
2,成功,没有竞争.失败存在竞争,膨胀成重量级锁.

原子操作的实现原理

1
2
3
4
5
cpu术语定义
缓存行 cache line
比较并替换 compare and swap
cp流水线 cpu pipiline
内存顺序冲突 memory order violation
1
2
3
4
cas的问题
1,aba ->版本解决
2,循环时间长开销大 -->jvm支持pause
3,只能保证一个共享变量的原子操作

内存模型

内存模型的基础

1
2
3
4
5
6
7
并发编程的两个关键问题:线程之间如何通信和线程之间如何同步.
通信指线程之间何种机制来交换信息.
命令式编程中,线程之间的通信机制由2种:共享内存和消息传递.

在共享内存的并发模型里.线程之间共享程序的公共状态,通过写-读内存中的公共状态进行隐式通信.在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过发送消息来显式进行通信.
同步指程序中用户控制不同线程间操作发生相对顺序的机制.在共享内存并发模型里,同步是显示进行的.程序员必须显示指定某个方法或某段代码需要在线程之间互斥执行.在消息传递并发模型里,由于消息的发送必须在消息的接收之前,因此同步是隐式进行的.
java都并发才用的是共享内存模型,java线程之间的通信总是隐式进行,整个通信过程对程序员完全透明.
1
2
主内存
工作内存

重排序

1
2
3
1,编译器优化的重排序
2,指令集并发的重排序
3,内存系统重排序

happens before

1
2
3
4
5
6
程序顺序规则:一个线程前边代码比之后的先执行
监视器锁规则:先加锁厚解锁
volatile规则:写在之后读
传递性:a->b,b->c a>c
start规则:a执行b.start(),那么a多任何操作比b.start()之前
join规则:a执行b.join()并成功返回,那麽b的操作在a.join()之前.

双重检查

1
2
3
4
5
重排序

解决办法
volatile
类初始化

java并发编程基础

线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
线程优先级
线程状态
new runnable blocked waiting time_waiting terminated

中断
中断可以理解为线程的一个标识位属性.

线程间通信
1,volatile和synchronized
2.等待/通知机制
3,等待/通知的经典范式
synchronized(对象){
whild(条件不满足){
对象.wait()
}
dohandle()
}



synchronized(对象){
改变条件
对象.notifyAll();

}

4,管道输入/输出流
PipedOutputStream,PipedInputStream PipedReader,PipedWriter
5,thread.join()的使用
6,threadlocal的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Lock
尝试非阻塞地获取锁
能被中断获取锁
超时获取锁


队列同步器aqs
AbstractQueuedSynchronizer


重入锁
公平锁/非公平锁


读写锁
公平性
重入
锁降级
状态(读16位写16位)




LockSupport工具
park()
parkNanos(nanos)
parkUnit
unpark(thread)



condition
获取锁后.才可使用condition
后端使用fifo队列
await()/notify()

await等待
会使当前线程进入等待队列并释放锁,同时线程状态变为等待状态.当从await()方法返回时,当前线程一定获取了conditon相关联动锁.
signal()通知
将会唤醒在等待队列中等待时间最长的节点(首节点),在唤醒节点之前,会将节点移到同步队列中.
signalAll()
看下

并发容器

concurrenthashmap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
jdk8
array+多个linkednode
单个使用cas
多个使用synchronized

添加然后统计
删除linkednode缩容

basecount+countsell
获取
https://www.cnblogs.com/keeya/p/9632958.html
//读取首节点的Node元素

/hash值为负值表示正在扩容,这个时候查的是ForwardingNode的find方法来定位到nextTable来
//eh=-1,说明该节点是一个ForwardingNode,正在迁移,此时调用ForwardingNode的find方法去nextTable里找。
//eh=-2,说明该节点是一个TreeBin,此时调用TreeBin的find方法遍历红黑树,由于红黑树有可能正在旋转变色,所以find里会有读写锁。
//eh>=0,说明该节点下挂的是一个链表,直接遍历该链表即可。


//既不是首节点也不是ForwardingNode,那就往下遍历
concurrentlinkedqueue
1
2
3
4
线程一个线程安全的队列有两种方式:一种是使用阻塞算法,另一种是使用非阻塞算法算法.
使用阻塞算法的队列可以用一个锁(出入一个锁)或两个锁(出入不同锁)等方式来实现.非阻塞的实现方式则可以使用循环cas的方式来实现.

是一个基于连接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,添加到队列的尾部,从头部获取.
阻塞队列
1
2
3
4
5
6
阻塞队列是一个支持两个附加操作的队列.
这两个附加的操作支持阻塞地超如和移除方法.
1)支持阻塞地插入方法
队列满,队列阻塞插入元素的线程,直到队列不满
2)支持阻塞地移除方法
队列为空时,获取元素的线程会等待队列变为非空.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ArrayBlockingQueue
数组/有界
公平/非公平
单锁
LinkedBlockingQueue
链表/有界
非公平单锁
PriorityBlockingQueue
支持优先级/无界
非公平单锁
PriorityQueue
DelayQueue
使用优先级队列实现的无界阻塞
非公平单锁
PriorityQueue
实现Delayed接口
SynchronousQueue
不存储元素的阻塞
公平/非公平
单锁
LinkedTransferQueue
链表/无界
LinkedBlockingDeque
链表/双向

原子操作类

1
2
3
4
原子基本类型
原子更新数组
原子更新引用类型
原子更新字段类

并发工具类

1
2
3
4
CountDownLatch
CyclicBarrier
Semaphore
Exchanger

线程池

1
2
3
4
5
6
7
8
9
10
11
min
max
休眠时间
时间单位
blockqueue
线程工厂
拒绝策略
AbortPolicy:直接抛出异常
CallerRunsPolicy:调用者线程运行
DiscardOldestPolicy:丢弃最近的一个任务,并执行当前任务
DiscardPolic:不处理丢弃掉
1
2
3
4
5
6
7
调用方法比较
feature
submit(runnable)
submit(runnable,result)
submit(callable)
void
execute(runnable)
1
2
3
4
5
6
7
8
9
10
11
关闭
无法中断的任务永远无法终止
shutdown
置为shutdown状态
中断空闲worker
不能中断的任务回继续执行
shutdownNow
置为stop状态
中断workers
清理未执行task
不能中断的任务,正在执行的任务继续执行
1
2
3
4
5
6
合理配置线程池
任务性质:cpu密集,io密集,混合型
cpu:n+1
内存=2n

需要根据实际情况来进行确定线程的数量和队列的大小
1
2
3
4
5
6
线程池监控
taskCount
completedTaskCount
largestPoolSize
getPolSize
getActiveCount

executor框架

1
2
3
4
5
ThreadPoolExecutor

FixedThreadPool
SingleThreadExecutor
CachedThreadPool

lock/synchronize区别

1
http://www.manongjc.com/article/47515.html
序号 lock synchronize
所处层面 java接口 java关键字 entry set(acquire)/wait set(release)
获锁方式 使用aqs,加锁当前线程,原子类统计加锁次数 实例方法/静态方法/代码块 获取监视器
获锁失败 加入aqs等待自旋,然后阻塞 加入虚拟等待队列中阻塞直到释放
偏向/重入 重入,原子数量变化 偏向,获取锁厚,再次不需要获取锁
lock独有队列 特有condition队列
解锁操作 使用unlock 自动解锁
并发demo
1
2
3
4
5
6
7
8
9
10
11
12
并发的问题
通信
volitate/syshorized
通知/等待
经典的通知等待
管道输入/输出流
thread.join
threadlocal
同步


乱序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Printer{
private string code;
private int num;
private LockObj lock;
public void run(){
if(num<10){
while(true){
if(!stringutil.equal(code,lock.lock)){
lock.wait();
}
}
dosomething();
lock.notifyAll();
}
}

publi class LockObj{
private volatie string code;
}


}