发表于: 2020-09-01 23:29:19

0 1534


今天完成的事情

1. SQL 子查询

2. wait、notify 协同


收获

1. SQL 子查询

使用子查询可以让我们从多个数据表中获取目标数据。子查询即嵌套在其他查询中的查询。

使用子查询:

假设现在有三张表,订单表、商品表、客户信息表,我们需要:

a,取出含有商品 RGAN01 的所有订单信息

b,在上一个结果集中取出下这些订单的所有用户 id

c,然后根据用户 id 取出客户信息

有了子查询之后我们可以这样做:

SELECT cust_name, cust_contact

FROM Customers

WHERE cust_id IN (SELECT cust_id 

                               FROM Orders

                               WHERE order_num IN (SELECT order_num

                                                                    FROM OrderItems

                                                                    WHERE prod_id="RGAN01"));


实际上上面的 SQL 是分为三个由里到外执行的。最里层的返回订单列表给中间层查询出用户的 id 信息,最后由最外层的查询语句返回我们需要的信息。

需要注意的是子查询的 SELECT 只能够查询单个列表,否则返回错误。


在子查询中使用计算字段

客户信息与订单信息在两张表中,我们需要展示每个客户的订单数。

SELECT cust_name, cust_state, (SELECT COUNT(*) 

                                 FROM Orders

                                 WHERE Orders.cust_id=Customers.cust_id) AS orders

FROM Customers;


这个子查询实际上执行了 4 次,因为搜索到了 4 位客户的信息。

上述 WHERE 子句的语义是在 Orders 表中找出与当前在 Customers 表中查询到的 cust_id 一致的订单数量。这个语句的逻辑和之前三层嵌套的 SELECT 语句不一样。



2. wait、notify 协同

在很早的 java 中式没有 JUC 包的,那么就需要使用 java 来实现一套线程协同方案。Object 中的 wait、notify 等方法正是在这个情境下产生的。


使用 wait 与 notify 实现一个生产者与消费者的模型,当消费者判断可以消费就进行消费并且 notify 生产者进行生产,否则就 wait。生产者判断可以进行生产就进行生产并且通知消费者来消费,否则就 wait。

import java.util.Random;

public class ObjectWait {

    public static void main(String[] argsthrows InterruptedException {
        ObjectWait objectWait = new ObjectWait();
        Container container = objectWait.new Container();
        Provider provider = objectWait.new Provider(container);
        Consumer consumer = objectWait.new Consumer(container);

        new Thread(consumer).start();
        new Thread(consumer).start();
        new Thread(consumer).start();
        new Thread(consumer).start();
        new Thread(consumer).start();
        new Thread(consumer).start();
        new Thread(consumer).start();
        new Thread(provider).start();
        new Thread(provider).start();
        new Thread(provider).start();
        new Thread(provider).start();
        new Thread(provider).start();
        new Thread(provider).start();
        new Thread(provider).start();

    }

    public class Container {
        volatile public Integer value = null;
    }

    public class Provider implements Runnable {

        private Container container;

        public Provider(Container container) {
            this.container = container;
        }

        @Override
        public void run() {
            synchronized (container) {
                while (container.value != null){
                    try {
                        container.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                container.value = new Random().nextInt();
                System.out.println("Provider >>>\t" + container.value);
                container.notifyAll();
            }

        }
    }

    public class Consumer implements Runnable {

        private Container container;

        public Consumer(Container container) {
            this.container = container;
        }

        @Override
        public void run() {
            synchronized (container) {
                while (container.value == null){
                    try {
                        container.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                System.out.println("Consumer <<<\t" + container.value);
                container.value = null;
                container.notifyAll();
            }
        }
    }
}

运行效果:


wait 与 sleep 的区别:

wait() 之后线程就不占用资源了,而是等待 notify 通知之后再来处理。sleep 会一直占用资源。


notify 与 notifyAll 的区别:

notify 只会通知等待队列里面的一个线程,而 notifyAll 会通知所有的线程。


为什么在 wait 外要使用 while 循环来判断条件,而不是 if:

这个在 Object 的 wait 方法注解上有一个解释:

* As in the one argument version, interrupts and spurious wakeups are
* possible, and this method should always be used in a loop:
* <pre>
*     synchronized (obj) {
*         while (&lt;condition does not hold&gt;)
*             obj.wait();
*         ... // Perform action appropriate to condition
*     }
* </pre>

我的理解是,如果把 wait 放在 if 里面,这个时候要是产生了虚假的唤醒,那么线程就顺着往下走了,跳过了条件的判断,这个逻辑就出问题了。

关于这个虚假的唤醒,我的理解有一种情况是 notifyAll 通知了所有的等待的生产者线程,但是可能只需要几个生产真就够了,这个时候如果不使用 while 的话,所有的生产真都会接着往下跑,逻辑出错。

但是如果是放在 while 里面,那么唤醒之后,还会进行条件的判断,这个时候各种意外的唤醒情况都会重新进入 wait 状态,直到真正的唤醒条件到来。





返回列表 返回列表
评论

    分享到