两个线程,可以操作初始值为0的一个变量,实现一个线程对该变量+1,一个线程对该变量-1,实现交替,来10轮,变量初始值为0,以实现此问题作为引入,简化我们的理解
文章目录
- 一、两个线程synchronized写法-结果无问题
 - 二(一)、四个线程synchronized写法-问题及解决办法
 - 二(二)、4线程问题 解决办法1:使用while进行条件判断
 - 二(三)、4线程问题 解决办法2:使用Lock和Condition实现线程间通信
 
一、两个线程synchronized写法-结果无问题
package com.atguigu.signcenter.thread;
/**
 * 线程之间的通信-两个线程synchronized写法
 * @author: jd
 * @create: 2024-09-02
 */
public class ThreadWaitNotifyDemo {
    public static void main(String[] args) {
        AirConditioner airConditioner = new AirConditioner();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    airConditioner.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    airConditioner.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
    }
}
class AirConditioner {  // 资源类
    private int number = 0;
    public synchronized void increment() throws InterruptedException {
        // 1. 判断
        if (number != 0) {
            this.wait();
        }
        // 2. 干活
        number++;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        // 3. 通知
        this.notifyAll();
    }
    public synchronized void decrement() throws InterruptedException {
        // 1. 判断
        if (number == 0) {
            this.wait();
        }
        // 2. 干活
        number--;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        // 3. 通知
        this.notifyAll();
    }
}
 
结果:
A	1
B	0
A	1
B	0
A	1
B	0
A	1
B	0
A	1
B	0
A	1
B	0
A	1
B	0
A	1
B	0
A	1
B	0
A	1
B	0
 
二(一)、四个线程synchronized写法-问题及解决办法
换成4个线程会导致错误,虚假唤醒。
原因:在java多线程判断时,不能用if,程序出事出在了判断上面,突然有一添加的线程进到if了,突然中断了交出控制权,没有进行验证,而是直接走下去了,加了两次,甚至多次。
中断和虚假唤醒是可能产生的,所以要用loop循环,if只判断一次,while是只要唤醒就要拉回来再判断一次。if换成while。(也就是说在四个线程下,有可能两个increment线程都在if中wait,当其被唤醒时,不会再次判断number是否满足条件,而直接执行number++,因此会导致number大于1的情况,同理也会出现number小于0的情况)
如果使用if判断,则会导致虚假唤醒:代码及现象
package com.atguigu.signcenter.thread;
/**
 * 线程之间的通信-两个、四个线程synchronized写法
 * @author: jd
 * @create: 2024-09-02
 */
public class ThreadWaitNotifyDemo {
    public static void main(String[] args) {
        AirConditioner airConditioner = new AirConditioner();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    airConditioner.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    airConditioner.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    airConditioner.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    airConditioner.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}
class AirConditioner {  // 资源类
    private int number = 0;
    public synchronized void increment() throws InterruptedException {
        // 1. 判断
        if (number != 0) {
            this.wait();
        }
        // 2. 干活
        number++;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        // 3. 通知
        this.notifyAll();
    }
    public synchronized void decrement() throws InterruptedException {
        // 1. 判断
        if (number == 0) {
            this.wait();
        }
        // 2. 干活
        number--;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        // 3. 通知
        this.notifyAll();
    }
}
 
现象
A	1
B	0
A	1
B	0
C	1
B	0
A	1
B	0
C	1
D	0
B	-1
B	-2
B	-3
B	-4
B	-5
B	-6
A	-5
D	-6
D	-7
D	-8
D	-9
D	-10
D	-11
D	-12
D	-13
D	-14
C	-13
A	-12
C	-11
A	-10
C	-9
A	-8
C	-7
A	-6
C	-5
A	-4
C	-3
A	-2
C	-1
 
图示为什么会出现问题
 在使用if判断两个线程的情况下,阻塞的线程只有两种情况,此时不会出现任何问题;
 而使用if在四个线程的情况下,可能存在这种情况:
- 最开始+线程进行了增加操作NotifyAll;
 - 此时+'线程抢占到执行权,进入if判断进入阻塞状态;
 - +线程又抢到了执行权,同样进入if判断阻塞;
 - -线程抢占执行权进行减操作,NotifyAll;
 - +'线程抢占执行权,进行增加操作,NotifyAll;
 - +线程抢占执行权,进行增加操作 (此时便出现了number=2的情况)
使用while就不会出现这种问题,因为在NotifyAll线程激活运行后,会进行二次判断!

 
二(二)、4线程问题 解决办法1:使用while进行条件判断
解决此问题:
 使用while进行条件判断
- 高内聚第耦合的前提下,线程操作资源类
 - 判断/干活/通知
 - 多线程交互中,必须要防止多线程的虚假唤醒,也即(在多线程的判断中不许用if只能用while)
解决代码: 
package com.atguigu.signcenter.thread;
/**
 * 线程之间的通信-两个、四个线程synchronized写法
 * @author: jd
 * @create: 2024-09-02
 */
public class ThreadWaitNotifyDemo {
    public static void main(String[] args) {
        AirConditioner airConditioner = new AirConditioner();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    airConditioner.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    airConditioner.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    airConditioner.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    airConditioner.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}
/*
class AirConditioner {  // 资源类
    private int number = 0;
    public synchronized void increment() throws InterruptedException {
        // 1. 判断
        if (number != 0) {
            this.wait();
        }
        // 2. 干活
        number++;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        // 3. 通知
        this.notifyAll();
    }
    public synchronized void decrement() throws InterruptedException {
        // 1. 判断
        if (number == 0) {
            this.wait();
        }
        // 2. 干活
        number--;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        // 3. 通知
        this.notifyAll();
    }*/
class AirConditioner {  // 资源类
    private int number = 0;
    public synchronized void increment() throws InterruptedException {
        // 1. 判断
        while (number != 0) {
            this.wait();
        }
        // 2. 干活
        number++;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        // 3. 通知
        this.notifyAll();
    }
    public synchronized void decrement() throws InterruptedException {
        // 1. 判断
        while (number == 0) {
            this.wait();
        }
        // 2. 干活
        number--;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        // 3. 通知
        this.notifyAll();
    }
}
 
正常结果:
A	1
B	0
A	1
B	0
A	1
B	0
C	1
B	0
A	1
B	0
C	1
B	0
A	1
D	0
C	1
B	0
A	1
D	0
C	1
B	0
A	1
D	0
C	1
B	0
A	1
D	0
C	1
B	0
A	1
D	0
C	1
D	0
A	1
D	0
C	1
D	0
C	1
D	0
C	1
D	0
 
二(三)、4线程问题 解决办法2:使用Lock和Condition实现线程间通信
通过Java8的Lock和Condition接口(await、signal、signalAll),可以替换synchronized与Object monitor方法(wait、notify、notifyAll)
 
 这里我们还是使用3.2中的例子,4个线程,两个打印1两个打印0,让其交替打印,分别打印十次
package com.atguigu.signcenter.thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * @author: jd
 * @create: 2024-09-02
 */
public class ThreadWaitNotifyDemo2 {
    public static void main(String[] args) {
        AirConditioner2 airConditioner = new AirConditioner2();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    airConditioner.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    airConditioner.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    airConditioner.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    airConditioner.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}
class AirConditioner2 {  // 资源类
    private int number = 0;
    // 使用java8 lock 和 condition接口实现
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    public void increment() throws InterruptedException {
        lock.lock();
        try {
            // 1. 判断
            while (number != 0) {
                condition.await(); // this.wait();
            }
            // 2. 干活
            number++;
            System.out.println(Thread.currentThread().getName() + "\t" + number);
            // 3. 通知
            condition.signalAll(); // this.notifyAll();
        }catch (Exception e) {
        }finally {
            lock.unlock();
        }
    }
    public void decrement() throws InterruptedException {
        lock.lock();
        try {
            // 1. 判断
            while (number == 0) {
                condition.await(); // this.wait();
            }
            // 2. 干活
            number--;
            System.out.println(Thread.currentThread().getName() + "\t" + number);
            // 3. 通知
            condition.signalAll(); // this.notifyAll();
        }catch (Exception e) {
        }finally {
            lock.unlock();
        }
    }
}
 
码字不易,请大家多多指教~




















