Java多线程之线程间的通信

1. 线程间的通信

  • 线程间通信

    • 生产者+消费者
    • 通知等待唤醒机制
  • 多线程编程模板

    • 判断 干活 通知
    • 判断需使用while,以防止中断和虚假唤醒(见java.lang.Object的API)

    A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied. In other words, waits should always occur in loops, like this one

       synchronized (obj) {
            while ()
                obj.wait(timeout);
            ... // Perform action appropriate to condition
       }
    

    线程也可以在没有通知、中断或超时的情况下被唤醒,这就是所谓的假唤醒。虽然这种情况在实践中很少发生,但应用程序必须通过测试导致线程被唤醒的条件来防止这种情况发生,如果条件不满足,则继续等待。换句话说,等待应该总是出现在循环中,就像这个循环一样

1.1 synchronized版

  • synchronized实现

    • 先2个线程操作资源类,资源中的操作判断使用if,如线程A和线程B,可以正常运行1 0 1 0 1 0...
    • 增加2个线程C和D,模拟虚假唤醒,判断依旧是if,运行的结果数字不是全部为1、0
      • 原因:在java多线程判断时,不能用if,程序出事出在了判断上面,突然有一添加的线程进到if了,突然中断了交出控制权,没有进行验证,而是直接走下去了,加了两次,甚至多次
    • 在4线程中,将资源类的if判断改为while判断,while是只要唤醒就要拉回来再判断一次
    package juc.demo;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @Description:
     *  现在两个线程,
     *  可以操作初始值为零的一个变量,
     *  实现一个线程对该变量加1,一个线程对该变量减1,
     *  交替,来10轮。
     * @Package: juc.demo
     * @ClassName NotifyWaitDemo
     * @author: wuwb
     * @date: 2020/10/19 13:30
     */
    public class NotifyWaitDemo {
        public static void main(String[] args) {
            int turn = 1000;
            //资源类
            ShareData data = new ShareData();
            new Thread(() -> {
                for (int i = 0; i < turn; i++) {
                    try {
                        data.increment();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, "A").start();
    
            new Thread(() -> {
                for (int i = 0; i < turn; i++) {
                    try {
                        data.decrement();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, "B").start();
    
            new Thread(() -> {
                for (int i = 0; i < turn; i++) {
                    try {
                        data.increment();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, "C").start();
    
            new Thread(() -> {
                for (int i = 0; i < turn; i++) {
                    try {
                        data.decrement();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, "D").start();
        }
    }
    
    //资源类
    class ShareData{
        private int number = 0;
    
        public synchronized void increment() throws InterruptedException {
            //判断  if换while
            while (number != 0) {
                this.wait();
            }
            //干活
            number++;
            System.out.println(Thread.currentThread().getName() + ":" + number);
            //通知
            this.notifyAll();
        }
    
        public synchronized void decrement() throws InterruptedException {
            while (number == 0) {
                this.wait();
            }
            number--;
            System.out.println(Thread.currentThread().getName() + ":" + number);
            this.notifyAll();
        }
    
    }
    
    

1.2 JUC版

1608004387960.png
  • Lock 及 Condition

    package juc.demo;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @Description:
     *  现在两个线程,
     *  可以操作初始值为零的一个变量,
     *  实现一个线程对该变量加1,一个线程对该变量减1,
     *  交替,来10轮。
     * @Package: juc.demo
     * @ClassName NotifyWaitDemo
     * @author: wuwb
     * @date: 2020/10/19 13:30
     */
    public class NotifyWaitDemo {
        public static void main(String[] args) {
            int turn = 1000;
            //资源类
            ShareData data = new ShareData();
            new Thread(() -> {
                for (int i = 0; i < turn; i++) {
                    try {
                        data.increment();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, "A").start();
    
            new Thread(() -> {
                for (int i = 0; i < turn; i++) {
                    try {
                        data.decrement();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, "B").start();
    
            new Thread(() -> {
                for (int i = 0; i < turn; i++) {
                    try {
                        data.increment();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, "C").start();
    
            new Thread(() -> {
                for (int i = 0; i < turn; i++) {
                    try {
                        data.decrement();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, "D").start();
        }
    }
    
    //资源类
    class ShareData{
        private int number = 0;
        private Lock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();
    
        public void increment() {
            lock.lock();
            try {
                //判断
                while (number != 0) {
                    condition.await();//this.wait();
                }
                //干活
                number++;
                System.out.println(Thread.currentThread().getName() + ":" + number);
                //通知
                condition.signalAll();//this.notifyAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void decrement() {
            lock.lock();
            try {
                while (number == 0) {
                    condition.await();
                }
                number--;
                System.out.println(Thread.currentThread().getName() + ":" + number);
                condition.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
    }
    
    

1.3 定制化调用通信

  • 使用Lock、Condition指定调用顺序,指定唤醒哪个线程
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Description
 * 多线程之间按顺序调用,实现A->B->C
 * ......来10轮
 */
public class ThreadOrderAccess {
    public static void main(String[] args) {
        ShareResource resource = new ShareResource();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                resource.printA();
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                resource.printB();
            }
        }, "B").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                resource.printC();
            }
        }, "C").start();
    }
}

class ShareResource{
    /**标志位*/
    private int number = 1;
    private Lock lock = new ReentrantLock();
    /**3把钥匙*/
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
   
    public void printA() {
        lock.lock();
        try {
            while (number != 1) {
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"==>AAAAAAAAAA");
            number = 2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printB() {
        lock.lock();
        try {
            while (number != 2) {
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"==>BBBBBBBBBB");
            number = 3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printC() {
        lock.lock();
        try {
            while (number != 3) {
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+"==>CCCCCCCCCC");
            number = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

你可能感兴趣的:(Java多线程之线程间的通信)