【线程池】深入理解Executors类

     在前面一篇文章线程池——深入理解ThreadPoolExecutor中,讲解了线程池的一种实现方法ThreadPoolExecutor类。而在Java中,Executors类也是一种常用的创建线程池的方式。

    Executors类可以用于方便的创建线程池。它为Executor,ExecutorService,ScheduledExecutorService,ThreadFactory和Callable类提供了一些工具方法。


Executors类创建的四种线程池

newCachedThreadPool

创建一个可按需自动扩容的线程池,但是会优先重用线程池中空闲可用的线程。这个类型的线程池将会大大提升执行许多短暂的异步任务的程序。如果线程池中线程都在使用,又有新任务到来,则新增一个线程到线程池。如果线程 60 秒内空闲,则将被终止移除线程池。corePoolSize 为 0,可知一旦线程 60s 空闲就会被移出线程池。缓存型线程池通常用于执行一些生存期很短的异步型任务,因此在一些面向连接的daemon型SERVER中用得不多。但对于生存期短的异步任务,它是Executors的首选。

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

将corePoolSize设置为0,maximumPoolSize设置为Integer.MAX_VALUE,使用了SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60s就销毁线程。

注意:虽然newCachedThreadPool中写着“会优先重用线程池中空闲的线程”,但是Thread的start()方法中又注明了多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。
在线程的操作中,如果

// T 是一个 Thread 的实现类 
T t = new T(); 
t.start(); 
t.start(); 
那么会抛出  java.lang.IllegalThreadStateException 异常。
但是这样写就可以
T t = new T(); 
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool(); 
newCachedThreadPool.submit(t); 
newCachedThreadPool.submit(t); 

查阅源码可以发现,newCachedThreadPool方法中使用的ThreadExecutorPool类的构造函数如下:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
线程池有一个 defaultThreadFactory , 实际也是每次 new 出来一个新的线程. 
ThreadFactory tf = Executors.defaultThreadFactory(); 
for (int i = 0; i < 10; i++) { 
    Thread tft = tf.newThread(t); 
    tft.start(); 
} 

newFixedThreadPool

定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。每次线程池中最多只有nThreads个线程

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

corePoolSize和maximumPoolSize的值都是创建线程池时传的参数,是一个定值。使用了LinkedBlockingQueue这个队列


newScheduledThreadPool 

计划线程池,支持定时及周期性任务执行。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue());
}

newSingleThreadExecutor 

单线程线程池,用唯一的线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

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

其实我们可以发现,这个方法就是把newFixedThreadPool(1)用FinalizableDelegatedExecutorService包装起来。那么newSingleThreadExecutor和newFixedThreadPool(1)有什么区别吗?具体分析请移步【线程池】Executors中的newSingleThreadExecutor和newFixedThreadPool(1)的区别


一般来说,CachedTheadPool在程序执行过程中通常会创建与所需数量相同的线程,然后在它回收旧线程时停止创建新线程,因此它是合理的Executor的首选,只有当这种方式会引发问题时(比如需要大量长时间面向连接的线程时),才需要考虑用FixedThreadPool。(该段话摘自《Thinking in Java》第四版)


从上面四个方法的源码我们可以发现,所有的方法都是调用了ThreadPoolExecutor的构造方法,通过传递不同参数来生成不同的线程池。Executors类的底层实现便是ThreadPoolExecutor。


最近阿里发布的 Java开发手册中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险

Executors利用工厂模式向我们提供了4种线程池实现方式,但是并不推荐使用,原因是使用Executors创建线程池不会传入这个参数而使用默认值所以我们常常忽略这一参数,而且默认使用的参数会导致资源浪费,不可取。


Executors类创建线程池方法

通用类,创建新线程

package threadTest;


public class ThreadPoolUtil implements Runnable{
	
	private Integer index;
	
	public ThreadPoolUtil(Integer index) {
		this.index = index;
	}
	
	@Override
	public void run() {
		try {
			System.out.println(index+"开始处理线程!");
			Thread.sleep(50);
			System.out.println("线程标识是:"+this.toString());
			System.out.println(index+"处理结束!");
		}
		catch(InterruptedException e) {
			e.printStackTrace();
		}
	}
	
}

newCachedThreadPool

package threadTest;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPools {

	public static void main(String[] args) {
		ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
		System.out.println("---------------newCachedThreadPool--------------");
		for(int i = 0; i < 4; i++) {
			final int index = i;
			newCachedThreadPool.execute(new ThreadPoolUtil(index));
		}
	}

}

结果:

---------------newCachedThreadPool--------------
0开始处理线程!
1开始处理线程!
2开始处理线程!
3开始处理线程!
线程标识是:threadTest.ThreadPoolUtil@49ce13ed
线程标识是:threadTest.ThreadPoolUtil@7e56d323
线程标识是:threadTest.ThreadPoolUtil@a8af3e0
1处理结束!
线程标识是:threadTest.ThreadPoolUtil@1ffffbce
2处理结束!
3处理结束!
0处理结束!
设置新建四个线程。由上面结果可知四个线程同时进行。


newFixedThreadPool

package threadTest;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPools {

	public static void main(String[] args) {
		ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(2);
		System.out.println("------------newFixedThreadPool-------------");
		for(int i = 0; i < 4; i++) {
			final int index = i;
			newFixedThreadPool.execute(new ThreadPoolUtil(index));
		}
	}

}

结果:

------------newFixedThreadPool-------------
1开始处理线程!
0开始处理线程!
线程标识是:threadTest.ThreadPoolUtil@69ab83b8
线程标识是:threadTest.ThreadPoolUtil@4cc1613c
1处理结束!
0处理结束!
2开始处理线程!
3开始处理线程!
线程标识是:threadTest.ThreadPoolUtil@676c60fd
3处理结束!
线程标识是:threadTest.ThreadPoolUtil@420686d4
2处理结束!

同样是四个线程,但是这时设置了线程固定大小是2,所以两个线程先新建,运行完毕后剩下的再执行。

newScheduledThreadPool


package threadTest;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ThreadPools {

	public static void main(String[] args) {
		ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(2);
		System.out.println("---------------newScheduledThreadPool--------------");
		for(int i = 0; i < 4; i++) {
			final int index = i;
			//延迟3秒执行
			newScheduledThreadPool.schedule(new ThreadPoolUtil(index),3,TimeUnit.SECONDS);
		}
	}
}

结果:

---------------newScheduledThreadPool--------------
0开始处理线程!
1开始处理线程!
线程标识是:threadTest.ThreadPoolUtil@12fbe3a
线程标识是:threadTest.ThreadPoolUtil@7afd6488
0处理结束!
1处理结束!
2开始处理线程!
3开始处理线程!
线程标识是:threadTest.ThreadPoolUtil@743a95a7
线程标识是:threadTest.ThreadPoolUtil@5102ac7
3处理结束!
2处理结束!

除了延迟3秒执行,别的和newFixedThreadPool相同

package threadTest;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ThreadPools {

	public static void main(String[] args) {
		ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
		System.out.println("---------------newSingleThreadExecutor--------------");
		for(int i = 0; i < 4; i++) {
			final int index = i;
			newSingleThreadExecutor.execute(new ThreadPoolUtil(index));
		}
	}

}

newSingleThreadExecutor 

package threadTest;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ThreadPools {

	public static void main(String[] args) {
		ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
		System.out.println("---------------newSingleThreadExecutor--------------");
		for(int i = 0; i < 4; i++) {
			final int index = i;
			newSingleThreadExecutor.execute(new ThreadPoolUtil(index));
		}
	}

}

结果:

---------------newSingleThreadExecutor--------------
0开始处理线程!
线程标识是:threadTest.ThreadPoolUtil@69ab83b8
0处理结束!
1开始处理线程!
线程标识是:threadTest.ThreadPoolUtil@676c60fd
1处理结束!
2开始处理线程!
线程标识是:threadTest.ThreadPoolUtil@6353e8c8
2处理结束!
3开始处理线程!
线程标识是:threadTest.ThreadPoolUtil@26516ac6
3处理结束!

每次只执行一个线程,并且是按顺序执行。


参考资料:Executors创建的4种线程池的使用

创建线程的几种方式


猜你喜欢

转载自blog.csdn.net/zxm490484080/article/details/80886243
今日推荐