一、普通版
synchronized版,使用wait、notify
public class Demo01 {
public static void main(String[] args) {
Shop shop = new Shop();
//生产者去提供商品
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
shop.buyGoods(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"生产者").start();
//消费者A去购买商品
new Thread(()->{
for (int i = 0; i < 50; i++) {
try {
shop.saleGoods(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"消费者A").start();
//消费者B去购买商品
new Thread(()->{
for (int i = 0; i < 50; i++) {
try {
shop.saleGoods(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"消费者B").start()
}
}
class Shop{
private int goodsNum = 50;
//
public synchronized void saleGoods(int num) throws InterruptedException {
while (goodsNum==0){
this.wait();
}
goodsNum -= num;
System.out.println(Thread.currentThread().getName()+"买走了"+num+"件商品,还剩余"+goodsNum+"件商品");
this.notifyAll();
}
public synchronized void buyGoods(int num) throws InterruptedException {
while (goodsNum!=0){
this.wait();
}
goodsNum += num;
System.out.println(Thread.currentThread().getName()+"送来了"+num+"件商品,还剩余"+goodsNum+"件商品");
this.notifyAll();
}
}
有可能出现的问题:虚假唤醒
表现:
虚假唤醒的表现为: 有时候资源会出现"超卖"现象. 也就是出现负数, 而这是不应该出现的. 关键点就是判断合法性的时候, 使用了if进行判断.
过程:
- 在消费者进入到购买方法中时,会告知商品为0,发生阻塞(wait)会将锁释放,这是其他消费线程也进入,发生阻塞,等到生产者将商品补充之后,再去进行商品的购买(num--),这时if只会判断一次,所以就可能发生num=-1;
解决办法:
将if换成while
二、高级版
使用Lock锁和 Condition
获取Condition的方法:
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition主要方法:
-
await():
等待 -
signal():
唤醒一个等待线程 -
signalAll():
唤醒全部等待线程
public class Test01 {
public static void main(String[] args) {
A a = new A();
new Thread(()->{for (int i = 0; i < 10; i++) {a.increment();}
},"线程A").start();
new Thread(()->{for (int i = 0; i < 10; i++) {a.decrement();}
},"线程B").start();
new Thread(()->{ for (int i = 0; i < 10; i++) { a.increment();}
},"线程C").start();
new Thread(()->{ for (int i = 0; i < 10; i++) {a.decrement(); }
},"线程D").start();
}
}
class A {
private int num = 0;
//获得锁
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();//获取Condition对象
public void increment() {
try {
lock.lock();
while (num!=0){
condition.await();//等待
}
num++;
System.out.println(Thread.currentThread().getName()+"->"+num);
condition.signalAll();//唤醒
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public synchronized void decrement() {
try {
lock.lock();
while (num==0){
condition.await();
}
num--;
System.out.println(Thread.currentThread().getName()+"->"+num);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
使用Condition精准通知和唤醒线程:
//有三个方法:
public void printA(){}//线程A执行
public void printB(){}//线程B执行
public void printC(){}//线程C执行
private Condition condition1 = lock.newCondition();//监听线程A
private Condition condition2 = lock.newCondition();//监听线程B
private Condition condition3 = lock.newCondition();//监听线程C
//在printA方法执行完之后,用Condition2去唤醒线程B执行priintB
//在printB方法执行完之后,用Condition3去唤醒线程B执行priintC
//在printA方法执行完之后,用Condition1去唤醒线程B执行priintA
//可以达到Condition精准的通知和唤醒线程
具体操作代码:
/**
* A执行完调用B,B执行完调用C C执行完调用A
*/
public class Test02 {
public static void main(String[] args) {
Data1 data1 = new Data1();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data1.printA();
}
},"A").start();
//线程B
new Thread(()->{
for (int i = 0; i < 10; i++) {
data1.printB();
}
},"B").start();
//线程C
new Thread(()->{
for (int i = 0; i < 10; i++) {
data1.printC();
}
},"C").start();
}
}
class Data1{
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();//监听线程A
private Condition condition2 = lock.newCondition();//监听线程B
private Condition condition3 = lock.newCondition();//监听线程C
private int num = 1;
public void printA(){
try {
lock.lock();
// 业务,判断-> 执行-> 通知
while (num!=1){
condition1.await();
}
num=2;
//通知condition2去唤醒线程B
condition2.signal();
System.out.println(Thread.currentThread().getName()+":->AAAA");
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printB(){
try {
lock.lock();
while (num!=2){
condition2.await();
}
num = 3;
//通知condition3去唤醒线程C
condition3.signal();
System.out.println(Thread.currentThread().getName()+":->BBBB");
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printC(){
try {
lock.lock();
while (num!=3){
condition3.await();
}
num = 1;
//通知condition1去唤醒线程A
condition1.signal();
System.out.println(Thread.currentThread().getName()+":->CCCC");
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}