线程池的单例构造与使用

1.做一个线程池的工具类(最简单的)

package cn.xdf.wlyy.thread;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


public class ThreadPool {
    // 单例,让池子只有一个  
    private ThreadPool() {  
    };  
  
    private static ThreadPool tp;  
  
    public static ThreadPool getInstance() {  
        if (tp == null) {  
            tp = new ThreadPool();  
        }  
        return tp;  
    }  
  
    // 构造一个任务池  
    // 参数说明:  
    // corePoolSize - 池中所保存的线程数,包括空闲线程。  
    // maximumPoolSize - 池中允许的最大线程数。  
    // keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。  
    // unit - keepAliveTime 参数的时间单位。  
    // workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。  
    // threadFactory - 执行程序创建新线程时使用的工厂。  
    // handler - 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。  
    ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(5));
  
    // 在池子里取一个线程执行任务  
    public void execute(Runnable task) {  
    	executor.execute(task);  
    } 
    
    public void shutdown() {
    	executor.shutdown();
    }
}

2.看是否需要返回值,不需要的话就实现Runnable接口,重写run方法;需要的话就要实现Callable接口,重写call方法。而run与call方法中是我们要多线程的业务代码


2.1不需要返回用这个类

package cn.xdf.wlyy.thread;

import cn.xdf.wlyy.util.DateUtil;


public class TestTask implements Runnable {  
    
    //如果我们的任务需要一些数据,可以通过构造方法传进来  
    String param;  
    public TestTask(String param){  
        this.param = param;  
    }  
      
    public void run() {
        System.out.println("正在执行task "+param + "," + DateUtil.getDateTime());
        try {
            Thread.currentThread().sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("task "+param+"执行完毕," + DateUtil.getDateTime());
    }  
    
    public static void main(String args[]){
        for(int i=0;i<15;i++){
        	//得到我们的池子  
        	ThreadPool tp = ThreadPool.getInstance();  
            //将我们的任务new出来  
            TestTask tt = new TestTask(i + "");  
            //用池子执行任务  
            tp.execute(tt); 
        }
        
    }  
  
} 

2.2需要返回的话用这个类

package cn.xdf.wlyy.thread;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

import cn.xdf.wlyy.util.DateUtil;

public class TestCallble implements Callable<String> {

	// 如果我们的任务需要一些数据,可以通过构造方法传进来
	String param;

	public TestCallble(String param) {
		this.param = param;
	}

	@Override
	public String call() throws Exception {
		System.out.println("正在执行task " + param + "," + DateUtil.getDateTime());
		try {
			System.out.println(Thread.currentThread().getName());
			Thread.currentThread().sleep(4000);
		}
		catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("task " + param + "执行完毕," + DateUtil.getDateTime());
		return param;
	}

	public static void main(String args[]) {
		for (int i = 0; i < 15; i++) {
			// 得到我们的池子
			ThreadPool tp = ThreadPool.getInstance();
			// 将我们的任务new出来
			TestCallble tt = new TestCallble(i + "");

			FutureTask<String> dbtask = new FutureTask<String>(tt);
			// 用池子执行任务
			tp.execute(dbtask);

			// 在这里可以做别的任何事情
			String result = null;
			try {
				// 取得结果,同时设置超时执行时间为1秒。同样可以用future.get(),不设置执行超时时间取得结果
				result = dbtask.get(3000, TimeUnit.MILLISECONDS);
				// result = dbtask.get();
			}
			catch (Exception e) {
				dbtask.cancel(true);
				// 超时后,进行相应处理
			}
			finally {
				System.out.println("task@" + i + ":result=" + result);
			}
		}

	}

}

值得说明的事,如果要返回值的话,只需调用futureTask.get()方法(可以设置等待时间),但如果添加了这个,那么call中的sleep必然要等,形成阻塞,一条条的获取调用结果值。而如果不加的话,就跟run方法一样,例如ThreadPoolExecutor中的参数corePoolSize(核心线程数)为5,正常来说5次一起执行,但加了get方法只能一次次执行。失去了多线程的意义了。

我也是初学,还在找解决办法...

<think>好的,我现在要帮用户理解如何用C++的模式实现数据库连接池和线程池。用户之前已经问过模式的使用场景,现在想具体了解实现方法,并且引用了站内的一些资料。首先,我需要结合用户提供的引用内容,整合出清晰的实现步骤,同时确保符合C++的最佳实践。 首先,用户提到数据库连接池和线程池都是通过池化技术来优化资源管理。模式在这里的作用是确保全局只有一个池实,避免资源浪费和冲突。根据引用[2]和[3],数据库连接池需要预先创建连接,按需分配,使用后归还。而线程池(引用[4])则是管理大量短任务,避免频繁创建销毁线程。 接下来,我需要考虑模式的实现方式,尤其是线程安全的问题。用户之前的回答提到过使用std::mutex和std::call_once,这里需要具体应用。对于数据库连接池,连接的数量管理也很重要,比如初始连接数、最大连接数,这些在引用[3]中都有提到。 然后,代码示部分需要展示模式的结构,比如getInstance方法,私有构造函数,以及资源管理的逻辑。对于线程池,可能需要展示如何添加任务到队列,并让工作线程处理任务。同时,要注意多线程环境下队列操作的同步,使用互斥锁和条件变量。 还要注意用户提到的注意事项,比如避免过度使用模式,生命周期管理等。在分布式系统中,可能不适用,但用户当前的问题不涉及这个,所以可以暂时不提。 最后,相关问题部分需要引导用户深入思考,比如线程安全的具体实现,静态类的区别,以及性能优化方法。这些都能帮助用户更全面地理解模式在池化技术中的应用。</think>以下是使用C++模式实现数据库连接池和线程池的详细方案,结合您提供的引用内容进行技术整合: ### 一、数据库连接池实现 $$ \text{连接池核心指标}= \begin{cases} initSize & \text{初始连接数} \\ maxSize & \text{最大连接数} \\ timeout & \text{获取超时时间} \end{cases} $$ ```cpp class DBConnectionPool { public: static DBConnectionPool& getInstance() { static DBConnectionPool instance; // C++11线程安全 return instance; } std::shared_ptr<MySQLConn> getConnection() { std::unique_lock<std::mutex> lock(mutex_); // 等待可用连接(带超时机制) if (!cond_.wait_for(lock, std::chrono::seconds(timeout_), [this]{ return !connections_.empty(); })) { throw std::runtime_error("获取数据库连接超时"); } auto conn = std::move(connections_.front()); connections_.pop_front(); return conn; } void returnConnection(std::shared_ptr<MySQLConn> conn) { std::lock_guard<std::mutex> lock(mutex_); connections_.push_back(conn); cond_.notify_one(); } private: DBConnectionPool() { // 初始化连接池 for(int i=0; i<initSize_; ++i) { connections_.emplace_back(std::make_shared<MySQLConn>()); } } std::deque<std::shared_ptr<MySQLConn>> connections_; std::mutex mutex_; std::condition_variable cond_; const int initSize_ = 10; // 初始连接数[^3] const int maxSize_ = 100; // 最大连接数[^3] const int timeout_ = 5; // 超时时间(秒) }; ``` **实现要点**: 1. 使用C++11的magic static特性保证线程安全[^4] 2. 通过`std::condition_variable`实现带超时的等待机制 3. 连接对象使用智能指针管理生命周期 4. 采用双端队列存储连接,提高存取效率 ### 二、线程池实现 $$ T_{task} = \frac{t_{create}}{n_{thread}} + t_{execute} $$ 其中$t_{create}$为线程创建时间,$n_{thread}$为线程数,$t_{execute}$为任务执行时间 ```cpp class ThreadPool { public: static ThreadPool& getInstance(int threadNum = std::thread::hardware_concurrency()) { static ThreadPool instance(threadNum); return instance; } template<typename F> void addTask(F&& task) { { std::lock_guard<std::mutex> lock(queueMutex_); tasks_.emplace(std::forward<F>(task)); } condition_.notify_one(); } private: ThreadPool(size_t threads) : stop_(false) { for(size_t i=0; i<threads; ++i) { workers_.emplace_back([this] { for(;;) { std::function<void()> task; { std::unique_lock<std::mutex> lock(this->queueMutex_); this->condition_.wait(lock, [this]{ return this->stop_ || !this->tasks_.empty(); }); if(this->stop_ && this->tasks_.empty()) return; task = std::move(this->tasks_.front()); this->tasks_.pop(); } task(); } }); } } ~ThreadPool() { { std::lock_guard<std::mutex> lock(queueMutex_); stop_ = true; } condition_.notify_all(); for(std::thread &worker : workers_) worker.join(); } std::vector<std::thread> workers_; std::queue<std::function<void()>> tasks_; std::mutex queueMutex_; std::condition_variable condition_; bool stop_; }; ``` **实现要点**: 1. 使用模板方法支持任意类型的任务[^4] 2. 通过条件变量实现任务队列的同步访问 3. 析构时自动回收所有线程资源 4. 默认线程数等于CPU核心数(通过`std::thread::hardware_concurrency()`) ### 三、联合使用 ```cpp // 数据库操作任务 auto dbTask = [](){ auto conn = DBConnectionPool::getInstance().getConnection(); conn->execute("UPDATE table SET status=1"); DBConnectionPool::getInstance().returnConnection(conn); }; // 提交到线程池 ThreadPool::getInstance().addTask(dbTask); ``` ### 四、性能优化策略 1. **动态扩容机制**:当连接池达到`maxSize`时,可创建临时连接(需在`getConnection()`中添加扩容逻辑)[^3] 2. **心跳检测**:定期检查连接有效性,自动重建失效连接 3. **任务优先级**:使用优先队列实现任务分级处理 4. **负载均衡**:采用轮询/随机算法分配连接资源
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值