此篇博客以 生产面包🍞/消费面包🍞例子为例,所以首先看一个synchronized(同步锁机制)(生产者 消费者)&等待唤醒机制的案例

 

synchronized(同步锁机制)(生产者 消费者)&等待唤醒机制的案例:

package android.java.thread20;

/**
 * 描述资源
 */
class Res {

    /**
     * name 是共享数据,被Thread-0 Thread-1公用使用
     */
    private String name;

    /**
     * id 是共享数据,被Thread-0 Thread-1公用使用
     */
    private int id;

    /**
     * flag 是共享数据,被Thread-0 Thread-1公用使用
     */
    private boolean flag; // 定义标记 默认第一次为false

    /**
     * 对操作共享数据的地方加入同步锁的方式来解决安全问题
     * public synchronized(this) void put(String name) {
     */
    public synchronized void put(String name) {

        /**
         * 生产之前判断标记
         */
        if (!flag) {

            // 开始生产
            id += 1;
            this.name = name + " 商品编号:" + id;
            System.out.println(Thread.currentThread().getName() + "生产者 生产了:" + this.name);
            // 生产完毕

            /**
             * 修改标记
             */
            flag = true;

            /**
             * 唤醒 wait(); 冻结的线程,如果没有就是空唤醒,Java是支持的
             */
            notify(); // 注意:⚠️ wait();  notify();  这些必须要有同步锁包裹着

            /**
             * 当前自己线程 冻结,释放CPU执行资格,释放CPU执行权,CPU就会去执行其他线程了
             */
            try {
                wait(); // 注意:⚠️ wait();  notify();  这些必须要有同步锁包裹着
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 对操作共享数据的地方加入同步锁的方式来解决安全问题
     * public synchronized(this) void put(String name) {
     */
    public synchronized void out() {

        /**
         * 消费之前判断标记
         */
        if (flag) {

            // 开始消费
            System.out.println(Thread.currentThread().getName() +  ">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 消费者 消费了:" + this.name);
            // 消费完毕

            /**
             * 修改标记
             */
            flag = false;

            /**
             * 唤醒 wait(); 冻结的线程,如果没有就是空唤醒,Java是支持的
             */
            notify(); // 注意:⚠️ wait();  notify();  这些必须要有同步锁包裹着

            /**
             * 当前自己线程 冻结,释放CPU执行资格,释放CPU执行权,CPU就会去执行其他线程了
             */
            try {
                wait(); // 注意:⚠️ wait();  notify();  这些必须要有同步锁包裹着
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

/**
 * 描述生产者任务
 */
class ProduceRunnable implements Runnable {

    /**
     * 此变量已经不是共享数据了,因为:
     *              new Thread(produceRunnable).start();
     *              new Thread(consumeRunnable).start();
     *
     * 所以:Thread-0有自己的res     Thread-1也有自己的res
     */
    private Res res;

    ProduceRunnable(Res res) {
        this.res = res;
    }

    /**
     * 执行线程任务
     */
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            res.put("面包🍞");
        }
    }
}

/**
 * 描述消费者任务
 */
class ConsumeRunnable implements Runnable {

    /**
     * 此变量已经不是共享数据了,因为:
     *              new Thread(produceRunnable).start();
     *              new Thread(consumeRunnable).start();
     *
     * 所以:Thread-0有自己的res     Thread-1也有自己的res
     */
    private Res res;

    ConsumeRunnable(Res res) {
        this.res = res;
    }

    /**
     * 执行线程任务
     */
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            res.out();
        }
    }
}

/**
 * 多线程通讯案例
 */
public class ThreadCommunicationDemo {

    public static void main(String[] args) {
        // 创建资源对象
        Res res = new Res();

        // 创建生产者任务
        ProduceRunnable produceRunnable = new ProduceRunnable(res);

        // 创建消费者任务
        ConsumeRunnable consumeRunnable = new ConsumeRunnable(res);

        // 启动生产者任务
        new Thread(produceRunnable).start();

        // 启动消费者任务
        new Thread(consumeRunnable).start();
    }

}

执行结果:

执行结果,和谐了 生产一个 消费一个:

 

 


 

 

以上的案例:synchronized(同步锁机制)(生产者 消费者)&等待唤醒机制的案例,是使用synchronized(同步锁🔒机制)中操作{ 锁.wait();   锁.notify().  锁.notifyAll(); }

   wait(); 等待/冻结 :可以将线程冻结,释放CPU执行资格,释放CPU执行权,并把此线程临时存储到线程池

   notify(); 唤醒线程池里面 任意一个线程,没有顺序;

   notifyAll(); 唤醒线程池里面,全部的线程;

   注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着,而同步锁🔒,就是synchronized(锁)

   

以下的案例:可以理解是 synchronized(同步锁机制)(生产者 消费者)&等待唤醒机制 的升级版,使用Lock接口来实现:同步锁机制/等待/唤醒

把synchronized等待唤醒机制的案例   修改成>>>    Lock等待唤醒机制的案例:

使用规则:必须使用 try {}finally{}  意思是:万一发生异常 或者 发生什么 ....... 导致没有释放锁🔒,从而引发严重错误❌, 加入以下代码控制的意思是:不管发生什么一定会释放锁🔒

     try {

        }finally {
            // 释放锁🔒
        }

 

 Condition监视器:可以操作等待唤醒机制

// 定义一个锁🔒      
       private Lock lock = new ReentrantLock();      

       // 定义监视器 监视器可以操作等待唤醒机制 
       private Condition condition = lock.newCondition();

       // 🔒锁定 锁住
       lock.lock();

       // 多线程/多地方调用 执行操作共享数据的代码 .......

       // 唤醒 
       condition.signal();

       // 等待
       condition.await();

       // 🔓释放锁
       lock.unlock();

 

使用Lock接口来实现:同步锁机制/等待/唤醒的案例:

package android.java.thread20;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 描述资源
 */
class Res {

    /**
     * name 是共享数据,被Thread-0 Thread-1公用使用
     */
    private String name;

    /**
     * id 是共享数据,被Thread-0 Thread-1公用使用
     */
    private int id;

    /**
     * flag 是共享数据,被Thread-0 Thread-1公用使用
     */
    private boolean flag; // 定义标记 默认第一次为false

    /**
     * Lock是接口不是直接实例化,需要实例化Lock的实现类ReentrantLock,这样就创建了一把锁🔒
     */
    private Lock lock = new ReentrantLock();

    /**
     * 定义监视器对象 注意:⚠ Lock可以绑定多个监视器,而synchronized只能绑定一个监视器,监视器可以等待/唤醒机制
     */
    private Condition condition = lock.newCondition();

    public void put(String name) {

        try {
            /**
             * 锁🔒定 操作共享数据的代码
             * 只要锁定后 不管CPU如何疯狂的切换执行,只要同步代码块里面的代码没有执行完,
             * 就不准其他线程进来执行,这样就保证了多线程操作共享数据的安全新
             */
            lock.lock();

            /**
             * 生产之前判断标记
             */
            if (!flag) {

                // 开始生产
                id += 1;
                this.name = name + " 商品编号:" + id;
                System.out.println(Thread.currentThread().getName() + "生产者 生产了:" + this.name);
                // 生产完毕

                /**
                 * 修改标记
                 */
                flag = true;

                /**
                 * 唤醒 冻结的线程,如果没有就是空唤醒,Java是支持的
                 * notify(); // 注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着,现在没有了synchronized就等于没有了锁,所以不能使用了,否则会报错
                 * condition.signal(); 的锁🔒是 定义的lock,没有lock就没有condition.signal();,这才是面向对象思想的体现
                 */
                condition.signal();

                /**
                 * 当前自己线程 冻结,释放CPU执行资格,释放CPU执行权,CPU就会去执行其他线程了
                 */
                try {
                    /**
                     * wait(); // 注意:⚠️ wait();  notify();  这些必须要有同步锁包裹着,现在没有了synchronized就等于没有了锁,所以不能使用了,否则会报错
                     * condition.await(); 的锁🔒是 定义的lock,没有lock就没有condition.await();,这才是面向对象思想的体现
                     */
                    condition.await();

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } finally {
            /**
             * 释放锁🔓,锁是资源
             * 释放锁🔒后 就不再持有锁🔒
             * 当不再持有锁🔒,其他然后线程都可以执行进入代码了
             *
             * 注意⚠:一旦锁定后,就一旦要释放锁,否则所以线程都进不了锁定的代码
             */
            lock.unlock();
            // System.out.println("释放锁🔒");
        }
    }

    public void out() {

        try {
            /**
             * 锁🔒定 操作共享数据的代码
             * 只要锁定后 不管CPU如何疯狂的切换执行,只要同步代码块里面的代码没有执行完,
             * 就不准其他线程进来执行,这样就保证了多线程操作共享数据的安全新
             */
            lock.lock();

            /**
             * 消费之前判断标记
             */
            if (flag) {

                // 开始消费
                System.out.println(Thread.currentThread().getName() +  ">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 消费者 消费了:" + this.name);
                // 消费完毕

                /**
                 * 修改标记
                 */
                flag = false;

                /**
                 * 唤醒 冻结的线程,如果没有就是空唤醒,Java是支持的
                 * notify(); // 注意:⚠️ wait(); notify(); 这些必须要有同步锁包裹着,现在没有了synchronized就等于没有了锁,所以不能使用了,否则会报错
                 * condition.signal(); 的锁🔒是 定义的lock,没有lock就没有condition.signal();,这才是面向对象思想的体现
                 */
                condition.signal();

                /**
                 * 当前自己线程 冻结,释放CPU执行资格,释放CPU执行权,CPU就会去执行其他线程了
                 */
                try {
                    /**
                     * wait(); // 注意:⚠️ wait();  notify();  这些必须要有同步锁包裹着,现在没有了synchronized就等于没有了锁,所以不能使用了,否则会报错
                     * condition.await(); 的锁🔒是 定义的lock,没有lock就没有condition.await();,这才是面向对象思想的体现
                     */
                    condition.await();

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } finally {
            /**
             * 释放锁🔓,锁是资源
             * 释放锁🔒后 就不再持有锁🔒
             * 当不再持有锁🔒,其他然后线程都可以执行进入代码了
             *
             * 注意⚠:一旦锁定后,就一旦要释放锁,否则所以线程都进不了锁定的代码
             */
            lock.unlock();
            // System.out.println("释放锁🔒");
        }
    }
}

/**
 * 描述生产者任务
 */
class ProduceRunnable implements Runnable {

    /**
     * 此变量已经不是共享数据了,因为:
     *              new Thread(produceRunnable).start();
     *              new Thread(consumeRunnable).start();
     *
     * 所以:Thread-0有自己的res     Thread-1也有自己的res
     */
    private Res res;

    ProduceRunnable(Res res) {
        this.res = res;
    }

    /**
     * 执行线程任务
     */
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            res.put("面包🍞");
        }
    }
}

/**
 * 描述消费者任务
 */
class ConsumeRunnable implements Runnable {

    /**
     * 此变量已经不是共享数据了,因为:
     *              new Thread(produceRunnable).start();
     *              new Thread(consumeRunnable).start();
     *
     * 所以:Thread-0有自己的res     Thread-1也有自己的res
     */
    private Res res;

    ConsumeRunnable(Res res) {
        this.res = res;
    }

    /**
     * 执行线程任务
     */
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            res.out();
        }
    }
}

/**
 * 多线程通讯案例
 */
public class ThreadCommunicationDemo {

    public static void main(String[] args) {
        // 创建资源对象
        Res res = new Res();

        // 创建生产者任务
        ProduceRunnable produceRunnable = new ProduceRunnable(res);

        // 创建消费者任务
        ConsumeRunnable consumeRunnable = new ConsumeRunnable(res);

        // 启动生产者任务
        new Thread(produceRunnable).start();

        // 启动消费者任务
        new Thread(consumeRunnable).start();
    }

}

执行结果,和synchronized案例一,一样的效果: