课程内容:
-
什么是并发错误?
-
如何解决并发错误?
-
什么是死锁?
-
如何解决死锁?
-
锁池和等待池的区别
什么是并发错误?
-
并发错误的概念
多个线程共享操作同一份数据的时候
线程体当中连续的多行操作 未必能够连续执行
很可能操作只完成另一部分 时间片就突然耗尽
此时 另一个线程抢到了时间片
直接访问了操作不完整的数据 -
*: 并发修改异常 和 并发错误 什么关系?
并发修改异常 是Sun公司官方的程序员
主动判断 主动校验 主动抛出的一个运行时异常
它的主要目的 就是为了避免程序进一步执行就会导致并发错误
最大限度的避免并发错误
(走在荒无人烟的小路上 为了不见鬼 选择拍晕自己..)
如何解决并发错误?
-
方式一:使用对象的互斥锁
互斥锁 = 锁标记 = 互斥锁标记 = 锁旗标 = 监视器 = Monitor
synchronized = 修饰符 = 形容词
1.修饰代码块
synchronized(临界资源){
需要连续执行的操作1;//
需要连续执行的操作2;
...;
}
2.修饰整个方法
public synchronized void add(Object obj){
需要连续执行的操作1;
需要连续执行的操作2;
...;
}
*: 哪怕synchronized加在方法修饰符上
也是对 对象进行加锁
*: Vector Hashtable StringBuffer 都是线程安全的
它们之所以线程安全 是因为
底层大量的方法使用了synchronized修饰
*: 单例模式的懒汉式 需要synchronzied修饰那个getter方法
*: synchronized 有一个其它修饰符都不具备的特性
它隔代丢失...
父类的synchronized方法子类能够继承吗?
能够继承得到这个方法 但是synchronized都不见了 -
synchronized的深度理解
*举例一
已知: Vector类的add() 和 remove() 都是synchronized修饰的
我们有一个Vector对象 名叫v
有两个线程对象 名叫 t1 和 t2
当t1线程调用v对象的add() 方法已经开始执行了
但是还没执行结束呢 此时时间片突然耗尽
t2线程抢到了时间片
问:
t2能不能调用v对象的add()? 不能
t2能不能调用v对象的remove()? 不能
*举例二
已知: Hashtable类的put() 和 remove() 都是synchronized修饰的
我们现在有两个Hashtable对象 名叫 h1 和 h2
有两个线程 名叫 t1 和 t2
当t1线程调用h1对象的put() 方法已经开始执行了
但是还没执行结束呢 此时时间片突然耗尽
而t2线程抢到了时间片
问:
t2线程能不能调用h1对象的put()? 不能
t2线程能不能调用h1对象的remove()? 不能
t2线程能不能调用h2对象的put()? 能
t2线程能不能调用h2对象的remove()? 能
t2线程能不能调用h2对象的xxxxx()? 能!
综上所述:
哪怕synchronized修饰符是加在方法签名上的
也是对 对象进行加锁
Java当中每个对象才有锁标记 加锁永远是对对象加锁
不存在对方法加锁的概念!! -
方式二:使用面向对象的可重入锁...since JDK5.0
java.util.concurrent.locks.ReentrantLock
Java包的 工具包的 并发包的 锁包的 可重入锁
lock() unlock()
加锁 释放锁
*:ReentrantLock 可以指定公平锁或非公平锁
new ReentrantLock(true);
*:公平 = 按照对锁资源申请的先后顺序依次获得
什么是死锁?
互斥锁标记使用过多或者使用不当
就会造成多个线程相互持有对方想要申请的资源
不释放的情况下 又去申请对方已经持有的资源
从而双双进入对方已经持有的资源的锁池当中
产生永久的阻塞
如何解决死锁?
一块空间和三个方法
一块空间: 等待池
三个方法: wait() notify() notifyAll()
wait() : 让当前线程放弃[调用方法的那个对象]的锁标记(主动进入阻塞状态)
并且进入那个对象的等待池当中阻塞
notify() : 从[调用方法的那个对象]的等待池
当中随机的唤醒一个线程
notifyAll() : 从[调用方法的那个对象]的等待池当中
唤醒所有阻塞的线程
*: 它们都必须在已经持有锁标记的前提下才能使用
也就是说它们一定出现在synchronized{}当中
也就是说 想要操作对象的等待池 必须先要拿到对象的锁标记
如果没有拿到锁标记 就直接尝试操作等待池
不但会操作失败 还会触发
IllegalMonitorStateException 运行时异常
*: 它们都是Object类的方法 不是Thread的方法
因为Java当中每个对象都有等待池
每个对象都可能需要操作等待池
所以这三个方法被定义在Object类当中
*举例:
顺丰陆运 送快递的 不能坐飞机
大货车不可能只配备一名司机
王师傅 马师傅
LeftThread RightThread
1.开4个小时
2.主动休息wait()
3.开4个小时
4.唤醒王师傅
5.主动休息wait()
6.唤醒马师傅
锁池和等待池的区别
锁池和等待池都是Java当中每个对象都有一份的空间标签:锁池,调用,synchronized,对象,day5,t2,线程 From: https://www.cnblogs.com/zhaodenghui33/p/18183307
锁池存放的是那些想要申请对象锁标记但是还没有得到的线程
等待池存放的是那些原本已经拿到对象锁标记
但是怕与其它线程相互阻塞 而又主动释放资源的线程
1.进入的时候是否需要释放锁标记
锁池 : 不释放锁标记
等待池 : 先释放持有锁标记
2.离开的时候是否需要调用方法
锁池 : 不需要操作
等待池 : 必须有其它线程notify()/notifyAll()
3.离开之后去往什么状态
锁池 : 返回就绪
等待池 : 进入锁池!!