`
2277259257
  • 浏览: 520085 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

阻塞队列+线程池

 
阅读更多

阻塞队列

阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来,如从队列中移除一个或者多个元素,或者完全清空队列,下图展示了如何通过阻塞队列来合作:

线程 1 往阻塞队列中添加元素,而线程 2 从阻塞队列中移除元素

从 5.0 开始,JDK 在 java.util.concurrent 包里提供了阻塞队列的官方实现。尽管 JDK 中已经包含了阻塞队列的官方实现,但是熟悉其背后的原理还是很有帮助的。

阻塞队列的实现

阻塞队列的实现类似于带上限的 Semaphore 的实现。下面是阻塞队列的一个简单实现

public class BlockingQueue {

private List queue = new LinkedList();

private int  limit = 10;

public BlockingQueue(int limit){

this.limit = limit;

}

public synchronized void enqueue(Object item)

throws InterruptedException  {

while(this.queue.size() == this.limit) {

wait();

}

if(this.queue.size() == 0) {

notifyAll();

}

this.queue.add(item);

}

public synchronized Object dequeue()

throws InterruptedException{

while(this.queue.size() == 0){

wait();

}

if(this.queue.size() == this.limit){

notifyAll();

}

return this.queue.remove(0);

}

}

必须注意到,在 enqueue 和 dequeue 方法内部,只有队列的大小等于上限(limit)或者下限(0)时,才调用 notifyAll 方法。如果队列的大小既不等于上限,也不等于下限,任何线程调用 enqueue 或者 dequeue 方法时,都不会阻塞,都能够正常的往队列中添加或者移除元素。

 

线程池

线程池(Thread Pool)对于限制应用程序中同一时刻运行的线程数很有用。因为每启动一个新线程都会有相应的性能开销,每个线程都需要给栈分配一些内存等等。

我们可以把并发执行的任务传递给一个线程池,来替代为每个并发执行的任务都启动一个新的线程。只要池里有空闲的线程,任务就会分配给一个线程执行。在线程池的内部,任务被插入一个阻塞队列(Blocking Queue),线程池里的线程会去取这个队列里的任务。当一个新任务插入队列时,一个空闲线程就会成功的从队列中取出任务并且执行它。

线程池经常应用在多线程服务器上。每个通过网络到达服务器的连接都被包装成一个任务并且传递给线程池。线程池的线程会并发的处理连接上的请求。以后会再深入有关 Java 实现多线程服务器的细节。

Java 5 在 java.util.concurrent 包中自带了内置的线程池,所以你不用非得实现自己的线程池。你可以阅读我写的java.util.concurrent.ExecutorService 的文章以了解更多有关内置线程池的知识。不过无论如何,知道一点关于线程池实现的知识总是有用的。

这里有一个简单的线程池实现:

public class ThreadPool {

  private BlockingQueue taskQueue = null;
  private List<PoolThread> threads = new ArrayList<PoolThread>();
  private boolean isStopped = false;

  public ThreadPool(int noOfThreads, int maxNoOfTasks) {
    taskQueue = new BlockingQueue(maxNoOfTasks);

    for (int i=0; i<noOfThreads; i++) {
      threads.add(new PoolThread(taskQueue));
    }
    for (PoolThread thread : threads) {
      thread.start();
    }
  }

  public void synchronized execute(Runnable task) {
    if(this.isStopped) throw
      new IllegalStateException("ThreadPool is stopped");

    this.taskQueue.enqueue(task);
  }

  public synchronized boolean stop() {
    this.isStopped = true;
    for (PoolThread thread : threads) {
      thread.stop();
    }
  }

}

(校注:原文有编译错误,我修改了下)

public class PoolThread extends Thread {

  private BlockingQueue<Runnable> taskQueue = null;
  private boolean       isStopped = false;

  public PoolThread(BlockingQueue<Runnable> queue) {
    taskQueue = queue;
  }

  public void run() {
    while (!isStopped()) {
      try {
        Runnable runnable =taskQueue.take();
        runnable.run();
      } catch(Exception e) {
        // 写日志或者报告异常,
        // 但保持线程池运行.
      }
    }
  }

  public synchronized void toStop() {
    isStopped = true;
    this.interrupt(); // 打断池中线程的 dequeue() 调用.
  }

  public synchronized boolean isStopped() {
    return isStopped;
  }
}

线程池的实现由两部分组成。类 ThreadPool 是线程池的公开接口,而类 PoolThread 用来实现执行任务的子线程。

为了执行一个任务,方法 ThreadPool.execute(Runnable r)用 Runnable 的实现作为调用参数。在内部,Runnable 对象被放入阻塞队列 (Blocking Queue) ,等待着被子线程取出队列。

一个空闲的 PoolThread 线程会把 Runnable 对象从队列中取出并执行。你可以在 PoolThread.run()方法里看到这些代码。执行完毕后,PoolThread 进入循环并且尝试从队列中再取出一个任务,直到线程终止。

调用 ThreadPool.stop()方法可以停止 ThreadPool。在内部,调用 stop 先会标记 isStopped 成员变量(为 true)。然后,线程池的每一个子线程都调用 PoolThread.stop()方法停止运行。注意,如果线程池的 execute()在 stop()之后调用,execute()方法会抛出 IllegalStateException 异常。

子线程会在完成当前执行的任务后停止。注意 PoolThread.stop() 方法中调用了 this.interrupt()。它确保阻塞在 taskQueue.dequeue() 里的 wait()调用的线程能够跳出 wait()调用(校对注:因为执行了中断 interrupt,它能够打断这个调用),并且抛出一个 InterruptedException 异常离开 dequeue()方法。这个异常在 PoolThread.run()方法中被截获、报告,然后再检查 isStopped 变量。由于 isStopped 的值是 true, 因此 PoolThread.run()方法退出,子线程终止。

分享到:
评论

相关推荐

    Linux下使用EPoll+队列+多线程的C++实现

    多线程在这里的作用是利用线程池来并行处理队列中的请求,提高服务器的并发处理能力。线程池是一种预先创建好一组线程的机制,当有任务需要执行时,可以从池中获取一个线程来执行任务,任务完成后线程返回池中,供...

    day19_阻塞队列、线程池、File类、递归.pdf

    本文主要讲解了Java中的阻塞队列、线程池以及File类的相关知识,并涉及到了递归的概念。阻塞队列是并发编程中的一种重要工具,它在多线程环境下的生产者-消费者模型中起到关键作用。线程池则是为了优化线程管理,...

    async多线程性能优化代码 + 线程池

    2. 工作队列:线程池维护一个工作队列,按照优先级和调度策略执行任务。这有助于平衡资源消耗和响应速度。 3. 自动调整:线程池会根据系统负载自动调整线程数量,避免过多线程导致的资源浪费。 四、性能优化 1. ...

    线程池.zip,互斥锁+条件变量+队列,实现线程池,包括线程池的创建,塞任务,和销毁线程池

    在本压缩包"线程池.zip"中,我们可以看到涉及到的核心概念有:线程池、互斥锁、条件变量以及队列。 线程池: 线程池是程序中预创建的一组可重用线程,这些线程在等待执行任务。当有新的任务到来时,线程池会挑选一...

    并发-线程池和阻塞队列.pdf

    线程池和阻塞队列是实现并发的关键组件之一。本文将详细介绍线程池原理、使用场景及注意事项,以及阻塞队列的相关知识。 首先,线程池是一种基于池化思想管理线程的技术,它可以重用一组线程执行多个任务。线程池的...

    并发-线程池和阻塞队列

    在Java编程中,"并发-线程池和阻塞队列"是两个核心概念,它们在多线程环境下处理任务调度和数据同步方面发挥着重要作用。线程池是一种管理线程资源的有效方式,而阻塞队列则常用于线程间通信和数据共享。 线程池...

    线程池&&队列各类区别使用场景

    线程池和队列在IT领域中是两个非常重要的概念,尤其在多线程编程和并发处理中扮演着核心角色。它们各自有独特的特性和适用场景,理解它们的区别和使用场景对于优化系统性能至关重要。 首先,让我们从线程池开始。...

    glib库异步队列和线程池代码分析

    ### glib库异步队列和线程池代码分析 #### 一、异步队列原理与实现 异步队列是一种高效的线程间通信机制,用于在多线程环境中同步共享数据。它通过将数据组织成队列的形式,允许线程在不阻塞的情况下进行读写操作...

    阻塞队列、线程池和四大函数式接口.md

    java进阶部分

    libthread_new.rar_linux 线程池_linux任务队列_线程池

    实现了一个线程池。其中运用了队列,以避免任务丢失。在队列和线程池之间创建了一个中间夹层,以提高可移植性。当任务来时,先压入队列,然后...线程完成任务后,再去队列查询,如果有任务就去执行,没有则阻塞,等待

    队列:队列在线程池等有限资源池中的应用.pdf

    - **策略选择**:线程池对于超出容量的任务有不同的处理策略,包括但不限于拒绝策略(如抛出异常)、等待策略(如阻塞队列)以及队列策略(如固定大小队列)。 - **阻塞队列**:阻塞队列是一种特殊的队列,当队列...

    epoll 多线程 线程池

    Java中的`java.util.concurrent.ThreadPoolExecutor`是实现线程池的一个常见类,其参数包括核心线程数、最大线程数、线程存活时间、工作队列等,可以根据实际需求进行配置。 学习epoll、多线程和线程池的知识,首先...

    09队列:队列在线程池等有限资源池中的应用.pdf

    线程池通常会维护一个任务队列,新任务将被加入到这个队列中排队,当线程池中的线程空闲下来,就从队列中取出任务进行执行。这样,即使资源有限,系统也可以有效地管理资源,保证任务不会丢失,同时避免了频繁地创建...

    09丨队列:队列在线程池等有限资源池中的应用1

    在实际应用中,队列的变种如循环队列、阻塞队列和并发队列在多线程编程、并发系统和操作系统中有着广泛的应用。例如,Disruptor是一个高性能的消息队列,利用循环队列实现零拷贝和最小锁竞争;Linux的环形缓存使用了...

    Android中的线程池与任务队列

    例如,我们可以通过`AsyncTask`配合线程池来处理耗时操作,避免阻塞主线程。`AsyncTask`内部就使用了线程池来管理后台任务,它的执行流程包括了`onPreExecute()`(在主线程运行)、`doInBackground()`(在工作线程...

    Python 使用threading+Queue实现线程池示例

    一、线程池 1、为什么需要使用线程池 1.1 创建/销毁线程伴随着系统开销,过于频繁的创建/销毁线程,会很大程度上影响处理效率。 记创建线程消耗时间T1,执行任务消耗时间T2,销毁线程消耗时间T3,如果T1+T3&gt;T2,那...

    支持多线程和泛型的阻塞队列

    1. **线程池**:任务调度器将任务放入队列,工作线程从队列中取出任务执行。 2. **网络编程**:接收线程将接收到的数据放入队列,处理线程从队列中取出数据进行解析。 3. **缓存系统**:新数据写入队列,读取操作从...

    BlockingQueue队列自定义超时时间取消线程池任务

    现在我们来详细讨论如何利用这些技术实现“BlockingQueue队列自定义超时时间取消线程池任务”。 首先,`BlockingQueue`是一个并发容器,它遵循先进先出(FIFO)原则,具有阻塞性质,当队列满时,生产者线程会被阻塞...

    java模拟阻塞队列

    Java中的阻塞队列是一种基于同步原语的高级数据结构,它在多线程编程中扮演着重要角色,尤其在并发处理和优化系统资源利用率...在实际项目中,阻塞队列常用于消息队列、线程池的工作队列等场景,是并发编程的重要工具。

    14-阻塞队列BlockingQueue实战及其原理分析二.pdf

    3. 根据线程池的需求选择合适的阻塞队列。 阻塞队列的优点: 1. 解耦:阻塞队列可以解耦生产者和消费者,使得他们可以独立运行。 2. 提高效率:阻塞队列可以提高系统的效率,因为它可以让生产者和消费者同时工作。...

Global site tag (gtag.js) - Google Analytics