Java 线程池

为什么要使用线程池:目前的大多数网络服务器,包括Web服务器、Email服务器以及数据库服务器等都具有一个共同点,就是单位时间内必须处理数目巨大的连接请求,但处理时间却相对较短。 传统多线程方案中我们采用的服务器模型则是一旦接受到请求之后,即创建一个新的线程,由该线程执行任务。任务执行完毕后,线程退出,这就是是“即时创建, 即时销毁”的策略。尽管与创建进程相比,创建线程的时间已经大大的缩短,但是如果提交给线程的任务是执行时间较短,而且执行次数极其频繁,那么服务器将处 于不停的创建线程,销毁线程的状态

线程池:是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务

线程池底层分析复用机制,使用BlockingQueue阻塞队列缓存线程。为什么使用阻塞队列,因为其他线程执行时需要时间的,在其他线程执行的时候,新线程需要等待,但是不能丢失,缓存到阻塞队列里。阻塞队列通过阻塞可以保留住当前想要继续入队的任务。当队列中没有线程的时候,去阻塞队列中去拿,会被阻塞,直到队列中有新的线程。

java中的线程池是通过Executor框架实现的,Executor 框架包括类:Executor,Executors,ExecutorService,ThreadPoolExecutor ,Callable和Future,FutureTask等。

线程池的优点

1,可以减少线程的创建和销毁的开销,性能佳。

2,提高响应的时间,当任务到达的时候(当前线程池中已经存在线程)任务可以不需要线程创建,就能执行。

3,统一分配和销毁,有规则的对创建的线程进行管理。

线程池的缺点

在不适合的场合下可能会比不使用线程池消耗更多的资源。

Executor: 所有线程池的接口,只有一个方法。Executor用于执行提交的Runnable任务。Executor提供了每个线程如何运行,线程的具体使用调度机制的解耦的一种方式。一般用Executor执行线程,而不是显示地创建线程

    public interface Executor {        
      void execute(Runnable command);      
    }

ExecutorService: 增加Executor的行为,是Executor实现类的最直接接口。

Executors: 提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService 接口。

线程池的分类以及创建

有四种线程池:可缓存的,定长的,定时的,单例的。

四种线程池的创建都是对ThreadPoolExecutor的构造的包装。

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

  1. corePoolSize  核心线程数,实际运行线程数
  2. maximumPoolSize最大线程数,最多可以创建多少线程。
  3. keepAliveTime (非核心线程)闲置线程最大存活时间。
  4. unit 超时时间的单位     有七种取值   (TimeUnit.DAYS;               //天
                                                                  TimeUnit.HOURS;             //小时
                                                                  TimeUnit.MINUTES;           //分钟
                                                                  TimeUnit.SECONDS;           //秒
                                                                  TimeUnit.MILLISECONDS;      //毫秒
                                                                  TimeUnit.MICROSECONDS;      //微妙
                                                                  TimeUnit.NANOSECONDS;       //纳秒)
  5. BlockingQueue  阻塞队列,是java.util.concurrent下的主要用来控制线程同步的工具。如果BlockQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态,直到BlockingQueue进了东西才会被唤醒。同样,如果BlockingQueue是满的,任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有空间才会被唤醒继续操作。
  6.   RejectedExecutionHandler handler   超出 maximumPoolSizes + workQueue 时,任务会交给RejectedExecutionHandler来处理

Executors.newCachedThreadPool();   创建一个无界的可缓存的线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

Executors.newFixedThreadPool(int nThreads)创建一个固定大小的线程池,控制线程最大并发数,超出的线程会在队列中等待

Executors.newScheduledThreadPool(int corePoolSize)创建一个定长线程池,支持定时及周期性任务执行。

Executors.newSingleThreadExecutor() 创建一个单线程的后台线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行

newCachedThreadPool

public class CacheThreadPool {
    public static void main(String[] args) {
		ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

		for (int i = 0; i < 100; i++) {
			final int count = i;
			cachedThreadPool.execute(new Runnable() {
				@Override
				public void run() {
					System.out.println(Thread.currentThread().getName() + ": i = " + count );
				}
			});
		}
		
		
	}
}

执行的结果显示:并不是申请要创建100个线程,就会执行100个线程。会有线程会进行缓存,然后进行复用。

可缓存的线程池:他是一个‘伪无界线程池’

  public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

他创建了一个核心线程为0,可创建最大线程数为整型量最大值,当非核心线程60秒之内没有需要处理的线程,则被回收。

此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。SynchronousQueue是一个是缓冲区为1的阻塞队列(同步移交)

要将一个元素放入SynchronousQueue中,必须有另一个线程正在等待接收这个元素。因此即便SynchronousQueue一开始为空且大小为1,第一个任务也无法放入其中,因为没有线程在等待从SynchronousQueue中取走元素。因此第一个任务到达时便会创建一个新线程执行该任务。

使用情况:对于大量短暂异步任务的程序来说,使用该线程池能够大大提高性能 。但是对于耗时的任务,阻塞队列长度为1,他的最大线程数是Integer.MAX_VALUE,所以当大量任务呗提交以后,会一直创建新的线程,消耗更多的资源。

newFixedThreadPool

 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

这是一个定长的线程池,构造的时候,传入参数,该参数即是核心线程数,也是最大线程数,并且空闲线程永远都不会被回收,他的阻塞队列是一个LinkedBlockingQueue,队列长度是整型量的最大值。所以该线程池可控制线程最大并发数。

newScheduledThreadPool

  public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    } 

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

参数与上边定长线程池一致,但是缓存队列他用的是DelayedWorkQueue,这是一个变长的队列,初始长度为16,每次增加自己的一半(newCapacity = oldCapacity + (oldCapacity >> 1)))是一个无界队列,它能按一定的顺序对工作队列中的元素进行排列。

特点:指定延时后执行任务。周期性重复执行任务。

  ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(3);
	  executor.scheduleAtFixedRate(new Runnable() {
		
		@Override
		public void run() {
			System.out.println("输出" + System.currentTimeMillis());
		}
	}, 0, 4, TimeUnit.SECONDS);

该线程是表明,创建3个个核心线程,并且提交的线程按照,0表示第一次执行时的延迟时间,4表示每经过4秒执行一次任务(指定延迟时间。重复执行);并且提交多个线程的时候,按一定的顺序执行。

newSingleThreadExecutor

 public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

LinkedBlockingQueue<Runnable>()也是一个"伪无界队列"。

这是一个单例的线程池,核心线程数和最大线程数都为1,也就是说,该线程池最大的并发量就是1,使用唯一的线程来保证所有请求都是按照先进先出的顺序来执行的。在线程执行的过程中,如果发生为异常,线程结束,会产生新的线程来执行后续的任务。并且在线程结束当前任务之后可以重用,而不会销毁。(在这里单例的线程池和我们普通的线程十分相似,而什么时候该起一个Thread,什么时候该使用单例的线程池呢。1,当线程所要执行的任务已经确定 2,线程不需要频繁的创建和销毁 3, 线程不需要被复用的时候,可以使用Thread

RejectedExecutionHandler (拒绝策略)

线程池中的拒绝策略有四种:拒绝策略是在(有界)队列满了,并且实际运行线程数大于最大可创建线程数时,线程池拒绝执行任务时的策略。

AbortPolicy  (中止策略) 默认策略:表示拒绝执行,抛出异常

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }

DiscardPolicy(抛弃策略):表示如果现在已经超过了最大线程数,则抛弃新提交的任务。而不做任何其他的事

 public DiscardPolicy() { }

        /**
         * Does nothing, which has the effect of discarding task r.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }

DiscardOldestPolicy(丢弃最老的):该策略表示,抛弃最老的哪个线程,也就是队列的头部的线程,腾出位置,将新提交的线程加到队列。

 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }

CallerRunsPolicy(调用者线程去执行):该策略表示,如果现在线程池已经不能执行时,该任务将由调用者线程去执行。

  public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值