【多线程:设计模式】享元模式-应用:自定义连接池

简介: 【多线程:设计模式】享元模式-应用:自定义连接池

【多线程:设计模式】享元模式-应用:自定义连接池

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 + '\'' +
                '}';
    }
}

结果

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循环外 就会导致连接池并没有空闲连接但是还是唤醒了所有线程
目录
相关文章
|
1月前
|
设计模式 存储 缓存
【设计模式】【结构型模式】享元模式(Flyweight)
一、入门 什么是享元模式? 享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享对象来减少内存使用,特别适用于存在大量相似对象的情况。 它的核心思想是将对象的内在状态(不变
85 16
|
1月前
|
设计模式 缓存 安全
【高薪程序员必看】万字长文拆解Java并发编程!(8):设计模式-享元模式设计指南
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的经典对象复用设计模式-享元模式,废话不多说让我们直接开始。
51 0
|
1月前
|
设计模式 运维 监控
并发设计模式实战系列(4):线程池
需要建立持续的性能剖析(Profiling)和调优机制。通过以上十二个维度的系统化扩展,构建了一个从。设置合理队列容量/拒绝策略。动态扩容/优化任务处理速度。检查线程栈定位热点代码。调整最大用户进程数限制。CPU占用率100%
152 0
|
2月前
|
Java
线程池是什么?线程池在实际工作中的应用
总的来说,线程池是一种有效的多线程处理方式,它可以提高系统的性能和稳定性。在实际工作中,我们需要根据任务的特性和系统的硬件能力来合理设置线程池的大小,以达到最佳的效果。
83 18
|
4月前
|
设计模式 存储 缓存
「全网最细 + 实战源码案例」设计模式——享元模式
享元模式(Flyweight Pattern)是一种结构型设计模式,旨在减少大量相似对象的内存消耗。通过分离对象的内部状态(可共享、不变)和外部状态(依赖环境、变化),它有效减少了内存使用。适用于存在大量相似对象且需节省内存的场景。模式优点包括节省内存和提高性能,但会增加系统复杂性。实现时需将对象成员变量拆分为内在和外在状态,并通过工厂类管理享元对象。
219 92
|
6月前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
6月前
|
数据采集 机器学习/深度学习 前端开发
PHP爬虫性能优化:从多线程到连接池的实现
本文介绍了一种通过多线程技术和连接池优化PHP爬虫性能的方法,以新浪投诉平台为例,详细展示了如何提高数据采集效率和稳定性,解决了传统单线程爬虫效率低下的问题。
232 2
PHP爬虫性能优化:从多线程到连接池的实现
|
7月前
|
设计模式 开发者 Python
Python编程中的设计模式应用与实践感悟####
本文作为一篇技术性文章,旨在深入探讨Python编程中设计模式的应用价值与实践心得。在快速迭代的软件开发领域,设计模式如同导航灯塔,指引开发者构建高效、可维护的软件架构。本文将通过具体案例,展现设计模式如何在实际项目中解决复杂问题,提升代码质量,并分享个人在实践过程中的体会与感悟。 ####
|
7月前
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
712 6
|
6月前
|
监控 Java 数据库连接
Java线程管理:守护线程与用户线程的区分与应用
在Java多线程编程中,线程可以分为守护线程(Daemon Thread)和用户线程(User Thread)。这两种线程在行为和用途上有着明显的区别,了解它们的差异对于编写高效、稳定的并发程序至关重要。
133 2

热门文章

最新文章