1. synchronized + wait + notifyAll
- synchronized:包裹着同步代码块,代码块执行完释放锁
- wait:线程挂起,失去锁
- notifyAll:唤醒同个对象的wait,此时wait所在线程处于争抢线程执行权的状态,如果抢到了,让wait所在的线程继续往下执行(wait代码的下一行代码开始)
- 为什么厨师(或者吃货)执行完synchronized同步代码块后(厨师和吃货的锁已经开启,都蓄势待发抢执行权)一定会继续执行第二次,而不会被已经唤醒的吃货(或者厨师)抢了执行权?因为厨师(或者吃货)处于while循环中,执行得更快。
以下代码证明上述四点:
2. Lock+Condition+await+signalAll
==========================================================
1. synchronized + wait + notifyAll
测试类
public class ThreadDemo {
public static void main(String[] args) {
//创建线程的对象
Cook c = new Cook();
Foodie f = new Foodie();
//给线程设置名字
c.setName("厨师");
f.setName("吃货");
//开启线程
c.start();
f.start();
}
}
厨师
public class Cook extends Thread{
@Override
public void run() {
int i = 0; //记录厨师第几次进锁
while (true){//线程c
synchronized (Desk.lock){
i++; //比如:厨师第1次进锁
System.out.println(getName() +"第"+ i+"次进锁");
if(Desk.count == 0){
break;
}else{
//判断桌子上是否有食物
if(Desk.foodFlag == 1){
System.out.println("cook waiting");
//如果有,就等待
try {
Desk.lock.wait();
//wait执行后下面代码不会执行,直到被notify,然后获得执行权后,
//才会在控制台打印厨师被唤醒了
System.out.println(getName()+"被唤醒了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
//如果没有,就制作食物
System.out.println("厨师做了一碗面条");
//修改桌子上的食物状态
Desk.foodFlag = 1;
//叫醒等待的消费者开吃
Desk.lock.notifyAll();
try {
//尽管notifyAll唤醒了wait中的吃货,但是厨师还
//是占用着锁,即使sleep了1秒也不会被蓄势待发的吃货抢到执行权
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
吃货
public class Foodie extends Thread{
@Override
public void run() {
int i = 0; //记录吃货第几次进锁
while(true){//线程f
synchronized (Desk.lock){
i++; //比如:吃货第1次进锁
System.out.println(getName() +"第"+ i+"次进锁");
if(Desk.count == 0){
break;
}else{
//先判断桌子上是否有面条
if(Desk.foodFlag == 0){
System.out.println("foodie waiting");
//如果没有,就等待
try {
Desk.lock.wait();//让当前线程跟锁进行绑定
//wait执行后下面代码不会执行,直到被notify,然后获得执行权后,
//才会在控制台打印吃货被唤醒了
System.out.println(getName()+"被唤醒了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
//把吃的总数-1
Desk.count--;
//如果有,就开吃
System.out.println("吃货在吃面条,还能再吃" + Desk.count + "碗!!!");
//吃完之后,唤醒厨师继续做
Desk.lock.notifyAll();
//修改桌子的状态
Desk.foodFlag = 0;
try {
//尽管notifyAll唤醒了wait中的厨师,但是吃货还
//是占用着锁,即使sleep了1秒也不会被蓄势待发的厨师抢到执行权
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
桌子 为了方便设置吃货能吃的面条为count=3
public class Desk {
//是否有面条 0:没有面条 1:有面条
public static int foodFlag = 0;
//总个数
public static int count = 3;
//锁对象
public static Object lock = new Object();
}
执行ThreadDemo 测试类,结果如下:
为什么厨师先执行?那是因为测试类中
//开启线程
c.start();
f.start();
厨师先start
厨师第1次进锁
厨师做了一碗面条 //此时厨师线程的synchronized同步代码块已经执行完毕!
//锁已经开启,此时厨师和吃货是同等概率争取执行权的,
//但是由于厨师处于while(true)死循环中,会比吃货更快
//抢到执行权,所以厨师第2次进锁。下面代码会证明。
//厨师唤醒吃货,第一次执行吃货还没有waiting
厨师第2次进锁
cook waiting //厨师释放锁,处于waiting中,
吃货第1次进锁
吃货在吃面条,还能再吃1碗!!! //吃完面条,同时唤醒厨师,厨师处于争抢执行权状态,
//但是抢不过现在的吃货,因为吃货处于死循环中,执行更快。
吃货第2次进锁 //同理,吃货虽然执行完了同步代码块,但是处于死循环中
foodie waiting //执行到wait代码,让吃货处于waiting状态
厨师被唤醒了 //上一次厨师waiting被唤醒后,抢到了执行权继续执行下一行代码
厨师第3次进锁
厨师做了一碗面条
厨师第4次进锁
cook waiting
吃货被唤醒了
吃货第3次进锁
吃货在吃面条,还能再吃0碗!!!
吃货第4次进锁
厨师被唤醒了
厨师第5次进锁
Process finished with exit code 0
证明死循环的线程更快,直接在厨师的同步代码块前增加一一行代码,或者sleep,延迟一下时间,看吃货有没有抢先一步。
厨师增加一行代码
public class Cook extends Thread{
@Override
public void run() {
int i = 0;
System.out.println(getName()+"蓄势待发,想要进锁");
while (true){//线程c
synchronized (Desk.lock){
i++;
System.out.println(getName() +"第"+ i+"次进锁");
if(Desk.count == 0){
break;
}else{
//判断桌子上是否有食物
if(Desk.foodFlag == 1){
System.out.println("cook waiting");
//如果有,就等待
try {
Desk.lock.wait();
System.out.println(getName()+"被唤醒了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
//如果没有,就制作食物
System.out.println("厨师做了一碗面条");
//修改桌子上的食物状态
Desk.foodFlag = 1;
//叫醒等待的消费者开吃
Desk.lock.notifyAll();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
运行结果:
厨师蓄势待发,想要进锁 测试类中,厨师先start
吃货第1次进锁 //由于打印了语句,厨师耽误了时间,给吃货抢先一步获得了执行权
foodie waiting
厨师第1次进锁
厨师做了一碗面条
厨师蓄势待发,想要进锁 //厨师蓄势待发,想进入第二次循环,却因为打印语句
//浪费了时间,被吃货抢到了执行权,如下
吃货被唤醒了
吃货第2次进锁
吃货在吃面条,还能再吃1碗!!!
吃货第3次进锁
foodie waiting
厨师第2次进锁
厨师做了一碗面条
吃货被唤醒了
厨师蓄势待发,想要进锁
吃货第4次进锁
吃货在吃面条,还能再吃0碗!!!
吃货第5次进锁
厨师第3次进锁
Process finished with exit code 0
=========================================================
2. Lock+Condition+await+signalAll
测试类
package com.cfj.lockstest;
public class ThreadDemo {
public static void main(String[] args) {
//创建线程的对象
Cook c = new Cook();
Foodie f = new Foodie();
//给线程设置名字
c.setName("厨师");
f.setName("吃货");
//开启线程
c.start();
f.start();
}
}
厨师
package com.cfj.lockstest;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Cook extends Thread {
@Override
public void run() {
int i = 0;
while (true) {//线程c
System.out.println(getName() + "蓄势待发,想要进锁");
//synchronized (Desk.lock){
//lock.lock();
Desk.lock.lock();
try {
i++;
System.out.println(getName() + "第" + i + "次进锁");
if (Desk.count == 0) {
break;
} else {
//判断桌子上是否有食物
if (Desk.foodFlag == 1) {
System.out.println("cook waiting");
//如果有,就等待
try {
//Desk.lock.wait();
Desk.condition.await();
System.out.println(getName() + "被唤醒了");
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
//如果没有,就制作食物
System.out.println("厨师做了一碗面条");
//修改桌子上的食物状态
Desk.foodFlag = 1;
//叫醒等待的消费者开吃
//Desk.lock.notifyAll();
Desk.condition.signalAll();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
Desk.lock.unlock();
}
// }
}
}
}
吃货
package com.cfj.lockstest;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Foodie extends Thread {
static Lock lock = new ReentrantLock();
@Override
public void run() {
/*
* 1. 循环
* 2. 同步代码块
* 3. 判断共享数据是否到了末尾(到了末尾)
* 4. 判断共享数据是否到了末尾(没有到末尾,执行核心逻辑)
* */
int i = 0;
while (true) {//线程f
//synchronized (Desk.lock){
//lock.lock();
Desk.lock.lock();
try {
i++;
System.out.println(getName() + "第" + i + "次进锁");
if (Desk.count == 0) {
break;
} else {
//先判断桌子上是否有面条
if (Desk.foodFlag == 0) {
System.out.println("foodie waiting");
//如果没有,就等待
try {
//Desk.lock.wait();//让当前线程跟锁进行绑定
Desk.condition.await();
System.out.println(getName() + "被唤醒了");
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
//把吃的总数-1
Desk.count--;
//如果有,就开吃
System.out.println("吃货在吃面条,还能再吃" + Desk.count + "碗!!!");
//吃完之后,唤醒厨师继续做
//Desk.lock.notifyAll();
Desk.condition.signalAll();
//修改桌子的状态
Desk.foodFlag = 0;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
Desk.lock.unlock();
}
// }
}
}
}
桌子
package com.cfj.lockstest;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Desk {
//是否有面条 0:没有面条 1:有面条
public static int foodFlag = 0;
//总个数
public static int count = 2;
//锁对象
// public static Object lock = new Object();
public static Lock lock = new ReentrantLock();
public static Condition condition = lock.newCondition();
}
运行结果:
厨师蓄势待发,想要进锁
吃货第1次进锁
foodie waiting
厨师第1次进锁
厨师做了一碗面条
厨师蓄势待发,想要进锁
吃货被唤醒了
吃货第2次进锁
吃货在吃面条,还能再吃1碗!!!
吃货第3次进锁
foodie waiting
厨师第2次进锁
厨师做了一碗面条
厨师蓄势待发,想要进锁
吃货被唤醒了
吃货第4次进锁
吃货在吃面条,还能再吃0碗!!!
吃货第5次进锁
厨师第3次进锁