• 【面试:并发篇36:多线程:设计模式】享元模式-应用:自定义连接池


    【面试:并发篇36:多线程:设计模式】享元模式-应用:自定义连接池

    00.前言

    如果有任何问题请指出,感谢。

    01.介绍

    我们现在设想一个场景,假如你有一个网站 QPS达到数千,如果每次都重新创建和关闭数据库连接池,性能会受到很大的影响,这时我们预先创建好一批连接,放入连接池。一次请求到达后从连接池获取连接,使用完毕后再还回连接池,这样即节约了连接和关闭的时间,也实现了连接的重用,不至于让庞大的连接数压垮数据库。

    02.连接池代码

    public class Test3 {
        public static void main(String[] args) {
            Pool pool = new Pool(2);
            for (int i = 0; i < 5; i++) {
                new Thread(() -> {
                    MockConnection conn = pool.borrow();
                    try {
                        Thread.sleep(new Random().nextInt(1000));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    pool.free(conn);
                }).start();
            }
        }
    }
    
    @Slf4j(topic = "c.Pool")
    class Pool {
        // 1. 连接池大小
        private final int poolSize;
    
        // 2. 连接对象数组
        private MockConnection[] connections;
    
        // 3. 连接状态数组 0 表示空闲, 1 表示繁忙
        private AtomicIntegerArray states;
    
        // 4. 构造方法初始化
        public Pool(int poolSize) {
            this.poolSize = poolSize;
            this.connections = new MockConnection[poolSize];
            this.states = new AtomicIntegerArray(new int[poolSize]);
            for (int i = 0; i < poolSize; i++) {
                connections[i] = new MockConnection("连接" + (i+1));
            }
        }
    
        // 5. 借连接
        public MockConnection borrow() {
            while(true) {
                for (int i = 0; i < poolSize; i++) {
                    // 获取空闲连接
                    if(states.get(i) == 0) {
                        if (states.compareAndSet(i, 0, 1)) {
                            log.debug("borrow {}", connections[i]);
                            return connections[i];
                        }
                    }
                }
                // 如果没有空闲连接,当前线程进入等待
                synchronized (this) {
                    try {
                        log.debug("wait...");
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        // 6. 归还连接
        public void free(MockConnection conn) {
            for (int i = 0; i < poolSize; i++) {
                if (connections[i] == conn) {
                    states.set(i, 0);
                    synchronized (this) {
                        log.debug("free {}", conn);
                        this.notifyAll();
                    }
                    break;
                }
            }
        }
    }
    
    class MockConnection {
    
        private String name;
    
        public MockConnection(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "MockConnection{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    结果

    18:28:02.779 c.Pool [Thread-0] - wait…
    18:28:02.778 c.Pool [Thread-3] - borrow MockConnection{name=‘连接2’}
    18:28:02.786 c.Pool [Thread-4] - wait…
    18:28:02.786 c.Pool [Thread-1] - wait…
    18:28:02.778 c.Pool [Thread-2] - borrow MockConnection{name=‘连接1’}
    18:28:03.325 c.Pool [Thread-3] - free MockConnection{name=‘连接2’}
    18:28:03.325 c.Pool [Thread-1] - borrow MockConnection{name=‘连接2’}
    18:28:03.325 c.Pool [Thread-4] - wait…
    18:28:03.325 c.Pool [Thread-0] - wait…
    18:28:03.519 c.Pool [Thread-1] - free MockConnection{name=‘连接2’}
    18:28:03.519 c.Pool [Thread-4] - wait…
    18:28:03.519 c.Pool [Thread-0] - borrow MockConnection{name=‘连接2’}
    18:28:03.521 c.Pool [Thread-2] - free MockConnection{name=‘连接1’}
    18:28:03.521 c.Pool [Thread-4] - borrow MockConnection{name=‘连接1’}
    18:28:03.554 c.Pool [Thread-4] - free MockConnection{name=‘连接1’}
    18:28:04.365 c.Pool [Thread-0] - free MockConnection{name=‘连接2’}

    解释
    MockConnection类

    连接池类

    Pool类

    1.states表示的是连接状态数组,如果第i个连接被用了就把states的cas方法把0 改成 1表示已经被用了
    2.borrow()方法 用来从连接池中获取连接里面是一个while循环 如果此时有空闲连接就返回空闲连接,如果没有则wait进入等待,等待之后的其他线程归还连接后唤醒
    3.free()方法 用来遍历当前连接池连接 如果当前线程归还的连接存在则 states.set(i, 0); 把这个连接重置为0 表示没有线程使用,并且notifyAll唤醒所有线程 然后其他在等待的线程被唤醒

    注意

    1.为什么borrow()方法 要进行wait 通过代码可知我们不加wait/notifyAll 这个代码一样没有问题,其实这里加wait的真正作用是防止while循环空转 降低cpu效率,加上wait后进入等待 不占用cpu时间片
    2.为什么free()方法 要在if里面执行notifyAll 而不放在for循环外,原因是 有这么一种可能 假如有人创建了一个不属于这个连接池的连接 然后归还了 如果此时notifyAll放在了for循环外 就会导致连接池并没有空闲连接但是还是唤醒了所有线程

  • 相关阅读:
    2023.10.02
    uvm通信
    el-table行添加阴影悬浮效果
    将windows的显示器作为linux的扩展屏
    海水稻种植面积超100万亩 国稻种芯-何登骥:四大类典型覆盖
    [矩阵的乘法运算] m*M = c
    RHCE. Stratis 管理分层存储
    Mendix 开发实践指南|Mendix的核心概念
    火车票识别易语言代码
    【无标题】后来,我认为王阳明比尼采,叔本华都高明
  • 原文地址:https://blog.csdn.net/m0_71229547/article/details/126080315