锁

# 互斥锁
# 1.显示锁 ReentrantLock (可重入锁)
# 特性
支持公平锁、非公平锁
- 公平锁 线程请求来了,只能在后面排队
- 非公平锁 线程要抢占cpu去处理
持有锁期间,可以被中断
支持主动放弃,tryLock
JDK支持,使用稍复杂,必须在finally中unLock()
指当前线程获取==同一个锁==时不会出现死锁。意味着当前线程获取相同的锁时再去获取锁不会被阻塞
# 2.内置锁 synchronized
# 特性
仅支持非公平锁
持有锁期间,不可中断
不支持主动放弃
Java内置,使用简单
锁的是类实例或者类对象
通过monitorenter与monitorexit指令保证原子性
总结:
互斥锁的使用 最为重要的是关注锁,临界区,资源的关系。
可见性问题一定要用HB规则推断

A通过上述代码转账给B,会出现线程不安全问题
如果把synchronized锁的粒度大一点的话就可以解决 上述图锁的是Accout.class
把临界区里面的共享变量balance锁住,才不会有问题
只有所有的共享变量共用一个锁,才能保证共享变量的读写不出现问题
# 死锁

# 线程饥饿死锁
# 如何避免死锁 Coffman条件
# 1.互斥:共享资源X和Y只能有一个被占用
方案:没有办法解决
# 2.占有且等待:线程T1已经取得共享资源X,在等待共享资源Y的时候,不释放共享资源X
方案:一次性申请所有资源
# 3.不可抢占:其他线程不能强行抢占线程T1占有的资源。
方案:主动放弃
实现:

如果tar.lock.tryLock()执行不成功,则主动放弃。
# 4.循环等待:线程T1等待线程T2占有的资源,线程T2等待线程T1占有的资源。
方案:资源排序
实现:A->B->C->A改为A->B->C
先申请A的锁,再申请B的锁,最后申请C的锁

# 管程
案例:
排队看病
科室1看完去科室2,科室2看完去科室3,科室3看完回科室1拿结果
给科室1建立一个病人队列,同时科室1只能容纳一个人在占用。科室2、3同理
并发条件下,同一时间的共享变量,只能由一个线程占有,其他线程只能等待(thread.wait())
代码:
static class monitor
{
private Item buffer[] = new Item [N] ;
private int count = 0;
public synchronized void insert (Item item)
{
.....
}
//每次只能有一个线程进入insert函数,如果多个线程同时调用insert函数,则后来者需要排队等待
}
2
3
4
5
6
7
8
9
10
11
# MySQL锁
# 乐观锁
当前线程每次去操作数据的时候都认为别人不会修改,更新的时候会判断别人是否会去更新数据,通过版本来判断,如果数据被修改了就拒绝更新,例如cas是乐
观锁,但是严格来说并不是锁,通过原子性来保证数据的同步,
# 版本号方式
通过在数据表中添加一个版本号字段,每次修改操作都会更新版本号的值。在提交修改时,检查当前读取的版本号与修改前读取的版本号是否一致,如果一致则说
明没有其他用户修改数据,可以正常提交修改;如果不一致,则说明有其他用户修改了数据,需要回滚事务或重新尝试。
# 时间戳方式
通过在数据表中添加一个时间戳字段,记录数据的最后修改时间。在提交修改时,检查当前读取的时间戳与修改前读取的时间戳是否一致,如果一致则说明没有其
他用户修改数据,可以正常提交修改;如果不一致,则说明有其他用户修改了数据,需要回滚事务或重新尝试。
# 悲观锁
当前线程去操作数据的时候,总是认为别的线程会去修改数据,所以每次操作数据的时候都会上锁,别的线程去操作数据的时候就会阻塞,比如synchronized