本文主要理解锁与这些方法的关系(参考了其他文章)
1.wait()和sleep()的区别
1.1 wait()
- wait()使当前线程阻塞,前提是必须先获得锁,一般配合synchronized 关键字使用,即一般在synchronized 同步代码块里使用 wait()、notify/notifyAll() 方法。
- wait()方法则是指当前线程让自己暂时退让出同步资源锁,以便其他正在等待该资源的线程得到该资源进而运行,只有调用了notify()方法,之前调用wait()的线程才会解除wait状态,可以去参与竞争同步资源锁,进而得到执行。(注意:notify的作用相当于叫醒睡着的人,而并不会给他分配任务,就是说notify只是让之前调用wait的线程有权利重新参与线程的调度)
1.2 sleep()
sleep()方法正在执行的线程主动让出CPU,在sleep指定时间后CPU再回到该线程继续往下执行(注意:sleep方法只让出了CPU,而并不会释放同步资源锁!!!)
2. notify()和notifyAll()的区别
notify()是唤醒一个线程,notifyAll()是唤醒全部线程,但是唤醒然后呢,不管是notify()还是notifyAll(),最终拿到锁的只会有一个线程,
3. notify()和notifyAll()和死锁的关系
之前我们先了解下锁的释放
当线程进入对象的synchronized代码块时,便占有了资源,直到它退出该代码块或者调用wait方法,才释放资源。
参考 https://www.jianshu.com/p/25e243850bd2?appinstall=0 的demo
package com.learn.LinkedT;import java.util.List;
import java.util.ArrayList;/*** 多线程之wait(),notify(),notifyall()*/
public class Something {private Buffer mBuf = new Buffer();public void produce() {synchronized (this) {while (mBuf.isFull()) {try {System.out.println(Thread.currentThread().toString()+"--------produce");wait();} catch (InterruptedException e) {e.printStackTrace();}}mBuf.add();// notifyAll();notify();}}public void consume() {synchronized (this) {while (mBuf.isEmpty()) {try {System.out.println(Thread.currentThread().toString()+"--------consume");wait();} catch (InterruptedException e) {e.printStackTrace();}}mBuf.remove();notify();
// notifyAll();}}private class Buffer {private static final int MAX_CAPACITY = 1;private List innerList = new ArrayList(MAX_CAPACITY);void add() {if (isFull()) {throw new IndexOutOfBoundsException();} else {innerList.add(new Object());}System.out.println(Thread.currentThread().toString() + " add");}void remove() {if (isEmpty()) {throw new IndexOutOfBoundsException();} else {innerList.remove(MAX_CAPACITY - 1);}System.out.println(Thread.currentThread().toString() + " remove");}boolean isEmpty() {return innerList.isEmpty();}boolean isFull() {return innerList.size() == MAX_CAPACITY;}}public static void main(String[] args) {final Something sth = new Something();Runnable runProduce = new Runnable(){int count = 4;public void run() {while (count-- >0){sth.produce();}}};Runnable runConsume = new Runnable() {int count = 4;public void run() {while (count-- > 0) {sth.consume();}}};for (int i = 0; i < 2; i++) {
// System.out.println("for--first---"+i);new Thread(runConsume).start();}for (int i = 0; i < 2; i++) {
// System.out.println("for-second---"+i);new Thread(runProduce).start();}}
}
使用notify出现死锁,分析下产生死锁的原因
生产者和消费者都是2个线程
线程0,1,2,3对应消费者1,消费者2,生产者1,生产者2
- 消费者1拿到锁----->buffer为空,wait()释放锁,现象1
- 生产者2拿到锁----->buffer为空,add,现象2,notify()
- 生产者2进入循环-->buffer满,wait(),释放锁,现象3,注意是同一个线程
- 生产者1拿到锁----->buffer满,wait(),现象4
- 消费者2拿到步骤一释放的锁----->buffer满,remove,现象5,notify()
- 消费者2进入循环---->buffer为空,wait(),释放锁,现象6
- 生产者2被唤醒---->buffer为空,生产,现象7,并进入循环,buffer满,现象8,wait()释放锁
- 生产者1拿到锁---->buffer满,现象9,wait(),释放锁
- 前面有两个notify,还有一个此时唤醒消费者1---->buffer满,remove,现象10,notify(),并进入循环,buffer空,wait(),现象11
- 消费者2被唤醒---->buffer为空,wait()
此时生产者和消费者都wait了!死锁形成,如果用notifyAll()就不会出现这种情况,因为wait的线程总会被唤醒,然后拿到锁