Java多线程和线程池(转)

1.为什么要使用线程池

在java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,服务器在创建和销毁线程上花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。为了防止资源不足,服务器应用程序需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利 
用已有对象来进行服务,这就是“池化资源”技术产生的原因。

线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。

2.线程池的组成部分

一个比较简单的线程池至少应包含线程池管理器、工作线程、任务列队、任务接口等部分。其中线程池管理器的作用是创建、销毁并管理线程池,将工作线程放入线程池中;工作线程是一个可以循环执行任务的线程,在没有任务是进行等待;任务列队的作用是提供一种缓冲机制,将没有处理的任务放在任务列队中;任务接口是每个任务必须实现的接口,主要用来规定任务的入口、任务执行完后的收尾工作、任务的执行状态等,工作线程通过该接口调度任务的执行。

线程池管理器至少有下列功能:创建线程池,销毁线程池,添加新任务。

工作线程是一个可以循环执行任务的线程,在没有任务时将等待。

任务接口是为所有任务提供统一的接口,以便工作线程处理。任务接口主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等。

3.线程池适合应用的场合

当一个服务器接受到大量短小线程的请求时,使用线程池技术是非常合适的,它可以大大减少线程的创建和销毁次数,提高服务器的工作效率。但是线程要求的运动时间比较长,即线程的运行时间比…….

以上信息来自如下文章:http://www.blogjava.net/stevenjohn/archive/2011/12/12/366161.html

一、Java自带线程池

先看看Java自带线程池的例子,开启5个线程打印字符串List:

package com.luo.test;

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

public class ThreadTest {

    public static void main(String[] args) {

        List<String> strList = new ArrayList<String>();
        for (int i = 0; i < 100; i++) {
            strList.add("String" + i);
        }
        int threadNum = strList.size() < 5 ? strList.size() : 5;
        ThreadPoolExecutor executor = new ThreadPoolExecutor(2, threadNum, 300,
                TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(3),
                new ThreadPoolExecutor.CallerRunsPolicy());
        for (int i = 0; i < threadNum; i++) {
            executor.execute(new PrintStringThread(i,strList,threadNum));
        }
        executor.shutdown();
    }
}

class PrintStringThread implements Runnable {

    private int num;

    private List<String> strList;

    private int threadNum;

    public PrintStringThread(int num, List<String> strList, int threadNum) {
        this.num = num;
        this.strList = strList;
        this.threadNum = threadNum;
    }

    public void run() {
        int length = 0;
        for(String str : strList){
            if (length % threadNum == num) {
                System.out.println("线程编号:" + num + ",字符串:" + str);
            }
            length ++;
        }
    }
}

Java自带线程池构造方法

ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue
RejectedExecutionHandler handler)

corePoolSize: 线程池维护线程的最少线程数,也是核心线程数,包括空闲线程
maximumPoolSize: 线程池维护线程的最大线程数
keepAliveTime: 线程池维护线程所允许的空闲时间
unit: 程池维护线程所允许的空闲时间的单位
workQueue: 线程池所使用的缓冲队列
handler: 线程池对拒绝任务的处理策略

当一个任务通过execute(Runnable)方法欲添加到线程池时:
1、 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
2、 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
3、如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
4、 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程 maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
5、 当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

事实上上面的例子代码写得有不足之处,如果你看出不足之处,说明你理解了线程池。否则可以多看几遍哦。

二、Spring线程池配置

3.1、直接调用

ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor();
//线程池所使用的缓冲队列
poolTaskExecutor.setQueueCapacity(200);
//线程池维护线程的最少数量
poolTaskExecutor.setCorePoolSize(5);
//线程池维护线程的最大数量
poolTaskExecutor.setMaxPoolSize(1000);
//线程池维护线程所允许的空闲时间
poolTaskExecutor.setKeepAliveSeconds(30000);
poolTaskExecutor.initialize(); 

3.2、通过配置文件

<bean id="poolTaskExecutor"      class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
   <!-- 核心线程数,默认为1 -->
   <property name="corePoolSize" value="5" />
   <!-- 最大线程数,默认为Integer.MAX_VALUE -->
   <property name="maxPoolSize" value="50" />
   <!-- 队列最大长度,一般需要设置值>=notifyScheduledMainExecutor.maxNum;默认为Integer.MAX_VALUE -->
   <property name="queueCapacity" value="2000" />
   <!-- 线程池维护线程所允许的空闲时间,默认为60s -->
   <property name="keepAliveSeconds" value="100" />
   <!-- 线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者 -->
   <property name="rejectedExecutionHandler">
       <!-- AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常 -->
       <!-- CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 -->
       <!-- DiscardOldestPolicy:抛弃旧的任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->
       <!-- DiscardPolicy:抛弃当前任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->
       <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
   </property>
</bean>
时间: 2024-10-12 12:36:57

Java多线程和线程池(转)的相关文章

JAVA多线程(三) 线程池和锁的深度化

github演示代码地址:https://github.com/showkawa/springBoot_2017/tree/master/spb-demo/src/main/java/com/kawa/thread 1.线程池 1.1 线程池是什么 Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池.在开发过程中,合理地使用线程池能够带来3个好处. 第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗. 第二:提高响应速度.当任务

java多线程、线程池的实现

Java实现多线程的3种方法:继承Thread类.实现runnable接口.使用ExecutorService,Callable.Future实现有返回值的多线程.前2种线程的实现方式没有返回值,第三种实现方式可以获取线程执行的返回值. 一:继承java.lang.Thread类 public class MyThread extends Thread { @Override public void run() { System.out.println( "my thread begin.&qu

java 多线程和线程池

● 多线程 多线程的概念很好理解就是多条线程同时存在,但要用好多线程确不容易,涉及到多线程间通信,多线程共用一个资源等诸多问题. 使用多线程的优缺点: 优点: 1)适当的提高程序的执行效率(多个线程同时执行). 2)适当的提高了资源利用率(CPU.内存等). 缺点: 1)占用一定的内存空间. 2)线程越多CPU的调度开销越大. 3)程序的复杂度会上升. 对于多线程的示例代码感兴趣的可以自己写Demo啦,去运行体会,下面我主要列出一些多线程的技术点. synchronized 同步块大家都比较熟悉

跟我学Java多线程——ThreadPoolExecutor(线程池)

什么是线程池 多线程开发中,由于线程数量多,并且每个线程执行一段时间就结束,所以要频繁的创建线程,但是这样频繁的创建线程会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间.在这种情况下,人们就想要一种可以线程执行完后不用销毁,同时该线程还可以去执行其他任务,在这样的情况下线程池就出现了. 线程池就是线程的池子,任务提交到线程池后,就从线程池中取出一个空闲的线程为之服务,服务完后不销毁该线程,而是将该线程还回到线程池中. 在线程池的编程模式下,任务是提交给整个线程池,而不是直接交给某个线程,

java 多线程以及线程池

1.多线程可以使程序反应更快,交互性更强,执行效率最高. 2.创建一个线程:  要实现Runnable 接口,创建Thread类的对象,用start开始执行线程. 3.使用Thread中的yield()方法为其他线程临时让出CPU时间. 4.sleep()方法可以将线程设置为休眠状态以确保其他线程执行.休眠时间为毫秒数·. sleep()方法可能抛出一个InterruptedException.这是一个必捡异常.需要try-catch块中. 5.java为每个线程都设置一个优先级.默认情况下,线

java多线程之线程池

自JDK5之后,Java推出了一个并发包,java.util.concurrent,在Java开发中,我们接触到了好多池的技术,String类的对象池.Integer的共享池.连接数据库的连接池.Struts1.3的对象池等等,池的最终目的都是节约资源,以更小的开销做更多的事情,从而提高性能. 我们的web项目都是部署在服务器上,浏览器端的每一个request就是一个线程,那么服务器需要并发的处理多个请求,就需要线程池技术,下面来看一下Java并发包下如何创建线程池. 1.  创建一个可重用固定

Java多线程(四) 线程池

一个优秀的软件不会随意的创建很销毁线程,因为创建和销毁线程需要耗费大量的CPU时间以及需要和内存做出大量的交互.因此JDK5提出了使用线程池,让程序员把更多的精力放在业务逻辑上面,弱化对线程的开闭管理. JDK提供了四种不同的线程池给程序员使用 首先使用线程池,需要用到ExecutorService接口,该接口有个抽象类AbstractExecutorService对其进行了实现,ThreadPoolExecutor进一步对抽象类进行了实现.最后JDK封装了一个Executor类对ThreadP

Java 多线程:线程池实现原理

前言 我们都知道,所谓线程池,那么就是相当于有一个池子,线程就放在这个池子中进行重复利用,能够减去了线程的创建和销毁所带来的代价.但是这样并不能很好的解释线程池的原理,下面从代码的角度分析一下线程池的实现. 线程池的相关类 对于原理,在 Java 中,有几个接口,类 值得我们关注: Executor ExecutorService AbstractExecutorService ThreadPoolExecutor Executor public interface Executor {    

java -&gt;多线程_线程池

线程池概念 线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源. 我们详细的解释一下为什么要使用线程池?(程序优化) 在java中,如果每个请求到达就创建一个新线程,开销是相当大的.在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多.除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源.如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或"