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

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

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

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循环外 就会导致连接池并没有空闲连接但是还是唤醒了所有线程
目录
相关文章
|
14天前
|
缓存 Java
深入理解Java并发编程:线程池的应用与优化
【5月更文挑战第30天】本文将深入探讨Java并发编程中的一个重要主题——线程池。我们将详细解析线程池的概念、应用及其优化方法,帮助读者更好地理解和使用线程池,提高程序的性能和效率。
|
28天前
|
设计模式 XML Java
第五篇 设计模式的选择和应用 - 智慧选择与合理实践
第五篇 设计模式的选择和应用 - 智慧选择与合理实践
|
10天前
|
安全 Java 开发者
Java中的线程同步:理解与应用
【6月更文挑战第3天】在Java的多线程编程中,线程同步是一个核心概念。它确保了在并发环境下,共享资源的访问是有序且安全的。本文将深入探讨Java线程同步的机制,包括synchronized关键字、Lock接口以及原子变量的使用,旨在帮助开发者更好地理解和应用这些工具,以编写高效且线程安全的代码。
|
13天前
|
安全 算法 Java
Java中的并发编程技术:解锁高效多线程应用的秘密
Java作为一种广泛应用的编程语言,其并发编程技术一直备受关注。本文将深入探讨Java中的并发编程,从基本概念到高级技巧,帮助读者更好地理解并发编程的本质,并学会如何在多线程环境中构建高效可靠的应用程序。
|
13天前
|
设计模式 开发框架 算法
C++中的设计模式:基本概念与应用
C++中的设计模式:基本概念与应用
23 2
|
14天前
|
设计模式 存储 前端开发
Java的mvc设计模式在web开发中应用
Java的mvc设计模式在web开发中应用
|
14天前
|
监控 算法 Java
深入理解Java并发编程:线程池的应用与优化
【5月更文挑战第30天】 在Java开发中,线程是实现并发处理的基础。然而,随着系统复杂性的增加和资源管理的需要,合理利用线程池已成为提升性能、保证系统稳定性的关键策略。本文将探讨线程池的核心原理,分析其应用场景,并提出优化线程池性能的实践建议,旨在帮助开发者更高效地使用Java进行并发编程。
|
15天前
|
Java 开发者
Java并发编程:理解线程池的工作原理与实践应用
【5月更文挑战第29天】在Java并发编程中,线程池是一种管理线程资源的重要工具。通过深入探讨线程池的工作原理和实践应用,本文将帮助开发者更好地理解和使用线程池,提高系统性能和稳定性。
|
15天前
|
存储 缓存 监控
深入理解Java并发编程:线程池的应用与优化
【5月更文挑战第29天】本文旨在探讨Java并发编程中的核心概念之一:线程池。我们将详细分析线程池的工作原理,应用场景以及性能优化策略。通过理论与实践相结合的方式,帮助读者更深入地理解线程池在多线程开发中的重要作用,并掌握如何高效地使用线程池以提高应用程序的性能和稳定性。
|
16天前
|
存储 缓存 监控
深入理解Java并发编程:线程池的应用与优化
【5月更文挑战第28天】本文将深入探讨Java并发编程中的重要概念——线程池。我们将从线程池的基本概念入手,详细介绍其工作原理、应用场景以及如何进行优化,以提高系统性能和稳定性。通过本文的学习,你将能够更好地理解和应用线程池,提升Java并发编程的能力。