欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java 线程池

程序员文章站 2022-05-04 17:53:54
...

为什么要使用线程池:目前的大多数网络服务器,包括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, 优先级)执行

Java 线程池

Java 线程池

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();
            }
        }