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

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

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

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循环外 就会导致连接池并没有空闲连接但是还是唤醒了所有线程
目录
相关文章
|
6天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
28天前
|
数据采集 机器学习/深度学习 前端开发
PHP爬虫性能优化:从多线程到连接池的实现
本文介绍了一种通过多线程技术和连接池优化PHP爬虫性能的方法,以新浪投诉平台为例,详细展示了如何提高数据采集效率和稳定性,解决了传统单线程爬虫效率低下的问题。
PHP爬虫性能优化:从多线程到连接池的实现
|
30天前
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
133 6
|
28天前
|
监控 Java 数据库连接
Java线程管理:守护线程与用户线程的区分与应用
在Java多线程编程中,线程可以分为守护线程(Daemon Thread)和用户线程(User Thread)。这两种线程在行为和用途上有着明显的区别,了解它们的差异对于编写高效、稳定的并发程序至关重要。
32 2
|
1月前
|
数据采集 存储 数据处理
Python中的多线程编程及其在数据处理中的应用
本文深入探讨了Python中多线程编程的概念、原理和实现方法,并详细介绍了其在数据处理领域的应用。通过对比单线程与多线程的性能差异,展示了多线程编程在提升程序运行效率方面的显著优势。文章还提供了实际案例,帮助读者更好地理解和掌握多线程编程技术。
|
1月前
|
存储 监控 安全
深入理解ThreadLocal:线程局部变量的机制与应用
在Java的多线程编程中,`ThreadLocal`变量提供了一种线程安全的解决方案,允许每个线程拥有自己的变量副本,从而避免了线程间的数据竞争。本文将深入探讨`ThreadLocal`的工作原理、使用方法以及在实际开发中的应用场景。
61 2
|
1月前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
61 6
|
1月前
|
设计模式 前端开发 JavaScript
JavaScript设计模式及其在实战中的应用,涵盖单例、工厂、观察者、装饰器和策略模式
本文深入探讨了JavaScript设计模式及其在实战中的应用,涵盖单例、工厂、观察者、装饰器和策略模式,结合电商网站案例,展示了设计模式如何提升代码的可维护性、扩展性和可读性,强调了其在前端开发中的重要性。
35 2
|
1月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
1月前
|
设计模式 监控 算法
Python编程中的设计模式应用与实践感悟###
在Python这片广阔的编程疆域中,设计模式如同导航的灯塔,指引着开发者穿越复杂性的迷雾,构建出既高效又易于维护的代码结构。本文基于个人实践经验,深入探讨了几种核心设计模式在Python项目中的应用策略与实现细节,旨在为读者揭示这些模式背后的思想如何转化为提升软件质量的实际力量。通过具体案例分析,展现了设计模式在解决实际问题中的独特魅力,鼓励开发者在日常编码中积极采纳并灵活运用这些宝贵的经验总结。 ###