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

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

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

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循环外 就会导致连接池并没有空闲连接但是还是唤醒了所有线程
目录
相关文章
|
5天前
|
设计模式 安全 数据库连接
后端开发中的设计模式应用
在软件开发的浩瀚海洋中,设计模式如同灯塔,为后端开发者指引方向。它们不仅仅是代码的模板,更是解决复杂问题的智慧结晶。本文将深入探讨几种常见的设计模式,包括单例模式、工厂模式和观察者模式,并揭示它们在实际应用中如何提升代码的可维护性、扩展性和重用性。通过实例分析,我们将一窥这些模式如何在后端开发中大放异彩,助力构建高效、灵活的软件系统。
|
10天前
|
缓存 监控 Java
Java中的并发编程:理解并应用线程池
在Java的并发编程中,线程池是提高应用程序性能的关键工具。本文将深入探讨如何有效利用线程池来管理资源、提升效率和简化代码结构。我们将从基础概念出发,逐步介绍线程池的配置、使用场景以及最佳实践,帮助开发者更好地掌握并发编程的核心技巧。
|
7天前
|
设计模式 数据库连接 PHP
PHP中的设计模式应用与最佳实践
在本文中,我们将探讨PHP设计模式的应用和最佳实践。通过深入分析,揭示如何在实际项目中有效利用设计模式来优化代码结构、提升系统灵活性和维护性,并分享一些常见设计模式的实际应用案例。无论你是PHP初学者还是经验丰富的开发者,这篇文章都会对你有所帮助。
|
6天前
|
Java 调度 开发者
Java中的多线程基础及其应用
【9月更文挑战第13天】本文将深入探讨Java中的多线程概念,从基本理论到实际应用,带你一步步了解如何有效使用多线程来提升程序的性能。我们将通过实际代码示例,展示如何在Java中创建和管理线程,以及如何利用线程池优化资源管理。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧,帮助你更好地理解和应用多线程编程。
|
16天前
|
存储 Java 程序员
优化Java多线程应用:是创建Thread对象直接调用start()方法?还是用个变量调用?
这篇文章探讨了Java中两种创建和启动线程的方法,并分析了它们的区别。作者建议直接调用 `Thread` 对象的 `start()` 方法,而非保持强引用,以避免内存泄漏、简化线程生命周期管理,并减少不必要的线程控制。文章详细解释了这种方法在使用 `ThreadLocal` 时的优势,并提供了代码示例。作者洛小豆,文章来源于稀土掘金。
|
25天前
|
设计模式 算法 开发者
深入理解工厂模式与策略模式:设计模式的灵活应用
深入理解工厂模式与策略模式:设计模式的灵活应用
|
3天前
|
设计模式 算法 PHP
PHP中的设计模式:策略模式的应用与实践
在软件开发中,设计模式是解决问题的最佳实践。本文将探讨PHP中的策略模式,通过实际应用案例,展示如何有效地使用策略模式来提高代码的灵活性和可维护性。我们将从基本概念入手,逐步深入到实际编码,最终实现一个具有策略模式的应用。
|
20天前
|
前端开发 C# 设计模式
“深度剖析WPF开发中的设计模式应用:以MVVM为核心,手把手教你重构代码结构,实现软件工程的最佳实践与高效协作”
【8月更文挑战第31天】设计模式是在软件工程中解决常见问题的成熟方案。在WPF开发中,合理应用如MVC、MVVM及工厂模式等能显著提升代码质量和可维护性。本文通过具体案例,详细解析了这些模式的实际应用,特别是MVVM模式如何通过分离UI逻辑与业务逻辑,实现视图与模型的松耦合,从而优化代码结构并提高开发效率。通过示例代码展示了从模型定义、视图模型管理到视图展示的全过程,帮助读者更好地理解并应用这些模式。
35 0
|
20天前
|
设计模式 安全 数据库连接
|
21天前
|
Java 程序员 调度
Java中的多线程基础与实战应用
【8月更文挑战第30天】在Java的世界里,多线程是提升程序性能的利器,但也是新手开发者常碰壁的难点。本文旨在通过浅显易懂的语言和生动的比喻,带领读者走进Java多线程的大门。我们将从线程的基本概念出发,逐步深入到线程的创建、启动、管理以及同步机制,最后通过一个简易版的图书管理系统实例,展示如何在实际开发中灵活运用多线程技术。