死锁
- 定义 多个线程各自占有一些公共资源,并且互相等待其它线程占有的的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情况,某一个同步块同时拥有“两个以上对象的锁”,就可能发生死锁问题。
图示
将上述图示代码化
//模拟口红
class Lipstick{
}
//模拟镜子
class Mirror{
}
//占有资源
class Makeup extends Thread{
//需求资只有一份所以置为静态
static Lipstick lipstick=new Lipstick();
static Mirror mirror=new Mirror();
int choice;
String girlName;
Makeup(int choice,String girlName){
this.choice=choice;
this.girlName=girlName;
}
public void run(){
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
//化妆
}
//互相持有对象的锁,也就是对方的资源
private void makeup() throws InterruptedException {
if (choice==0){
synchronized (lipstick){
System.out.println(this.girlName+"获得口红的锁");
Thread.sleep(1000);
//一秒钟后想获得镜子的锁
synchronized (mirror){
System.out.println(this.girlName+"获得镜子的锁");
}
}
}
else{
synchronized (mirror){
System.out.println(this.girlName+"获得镜子的锁");
Thread.sleep(2000);
//一秒钟后想获得镜子的锁
synchronized (lipstick){
System.out.println(this.girlName+"获得口红的锁");
}
}
}
我们让两个线程同时跑
Makeup her1=new Makeup(0,"灰姑娘");
Makeup her2=new Makeup(1,"白雪公主");
有问题的商量解决 ,用完资源之后将占有的锁让出不就实现了解死锁了吗?
- 总结死锁:
- 互斥条件一个资源每次只能被一个进程使用
- 请求与保持条件:一个进程因请求资源而阻塞时,对已经获得的资源保持不
- 不剥夺条件:进程已经获得的资源,在未使用之前,不可以进行强行剥夺
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源的关系。
解决办法:想办法破坏其中一个
lock锁
- 关于lock锁
- 更加强大的线程同步机制–显示定义同步锁来对对象实现同步
- locks.lock接口是控制多个线程对共享资源进行访问的工具,所提供了对对象资源的独占访问,每次只能有一个线程对lock对象加锁,线程访问共享资源之前应先获得Lock对象
- ReentrantLock 类实现了Lock ,他拥有了与synchronized 相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁,释放锁.
让我们再来回忆一下买票的问题
在上一篇中我们尝试用了synchronized 同步方法来解决线程不安全的情况这次我们用一个Lock锁再来解决一次
定义一把锁
private final ReentrantLock lock=new ReentrantLock();
在代码块之间加锁 ->开锁
lock.lock();
//代码区
lock.unlock();
双锁对比 : synchronized &&lock
- lock是显示锁需要进行手动开启关闭,synchronized是隐式锁 除了作用域自动释放
- lock锁只有代码块,synchronized有代码块锁和方法锁
- 使用lock锁 JVM将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性 提供更多子类
- 优先使用顺序:lock> 同步块> 同步方法
线程协作:生产者消费者模式
线程通信
应用场景:生产者和消费者问题
- 假设一个中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中的产品取走消费.
- 如果仓库没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止
- 如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费等待,直到仓库中再次放入产品为止
线程通信分析
生产者和消费者之间共享同一个资源,并且生产者和消费者之间互为依赖,互为条件。
- 对于生产者,没有生产产品之前要通知消费者等待,而生产了产品之后,又要马上通知消费者消费。
- 对于消费者,在消费者之后,要通知生产者已经结束消费,需要生产新的产品以供消费
- 在生产者消费者问题中,仅有synchronized是不够的
- synchronized可阻止并发更新同一个共享资源,实现了同步。
- synchronized不能用来实现不同线程之间的消息传递
方法名 | 作用 |
---|---|
wait() | 表示线程一直等待,直到其它线程通知,与sleep不同会释放锁 |
wait(long time out) | 指定等待的毫秒数 |
notify() | 唤醒一个正在处于等待状态的线程 |
notifyall() | 唤醒同一对象上所有调用wait()方法的线程,优先级别高的优先调度 |
均是Object类的方法 ,都只能在同步方法或者同步代码块中使用
解决方式一:
并发协作模型 生产者/消费者模式—>管程法
- 生产者:负责生产数据的模块
- 消费者:负责处理数据的模块
- 缓冲区:消费者不能直接使用消费者的数据,他们之间得有个缓冲区
上图
解决方式二:
并发协作模型:生产者/消费者模式—>信号灯法