目录
3.1.5newSingleThreadScheduledExecutor
1.线程池优势
- 降低资源的消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。当任务到达时,任务可以不需要等到线程池创建就能立即执行。
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配,调优和监控。但是要做到合理利用线程池,必须对其实现原理了如指掌。
过多的创建线程,会使得内存耗尽 java.lang.OutOfMemoryError: unable to create new native thread
2.相关源码
合理配置:
CPU密集(计算):线程数和CPU核数相等
IO密集(读写): 2倍的CPU核数
Executors
public class Executors {
// 线程工厂
public static ThreadFactory defaultThreadFactory();
// 创建固定数量的线程池,如果没有任务,那么线程一直等待, n一般与cpu个数一致(Runtime.getRuntime().availableProcessors())
// 最为常用
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
// 创建一个可缓存的线程池
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
// 创建只有一个线程的线程池,所有提交的线程是顺序执行的
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
// 创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
}
public interface ExecutorService extends Executor {
// 提交可运行任务供执行,并返回表示该任务的未来。未来的get方法将在成功完成后返回
Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);
<T> Future<T> submit(Callable<T> task);
// 停止线程池接收新的线程,并且执行完已经接收的线程,执行完毕后结束掉线程池(如果不结束掉可以看到main方法会一直运行不会结束,生成上一般都是保持一直运行不需要关闭的)
void shutdown();
}
public interface ThreadFactory {
// 将Runnable包装成Thread
Thread newThread(Runnable r);
}
public interface Executor {
void execute(Runnable command);
}
Callable 的 call()方法只能通过 ExecutorService 的 submit(Callable task) 方法来执行,并且返回一个 Future,是表示任务等待完成的 Future。
当将一个 Callable 的对象传递给 ExecutorService 的 submit 方法,则该 call 方法自动在一个线程上执行,并且会返回执行结果 Future 对象。同样,将 Runnable 的对象传递给 ExecutorService 的 submit 方法,则该 run 方法自动在一个线程上执行,并且会返回执行结果 Future 对象,但是在该 Future 对象上调用 get 方法,将返回 null。
submit首先选择空闲线程来执行任务,如果没有,才会创建新的线程来执行任务
ExecutorService 的 shutdown()方法来平滑地关闭 ExecutorService,调用该方法后,将导致 ExecutorService 停止接受任何新的任务且等待已经提交的任务执行完成(已经提交的任务会分两类:一类是已经在执行的,另一类是还没有开始执行的),当所有已经提交的任务执行完毕后将会关闭 ExecutorService。因此我们一般用该接口来实现和管理多线程。
ExecutorService 的生命周期包括三种状态:运行、关闭、终止。创建后便进入运行状态,当调用了 shutdown()方法时,便进入关闭状态,此时意味着 ExecutorService 不再接受新的任务,但它还在执行已经提交了的任务,当素有已经提交了的任务执行完后,便到达终止状态。如果不调用 shutdown()方法,ExecutorService 会一直处在运行状态,不断接收新的任务,执行新的任务,服务器端一般不需要关闭它,保持一直运行即可。
3.实例
3.1Runable示例
3.1.1ThreadFactory
public static void main(String[] args) {
ThreadFactory threadFactory = Executors.defaultThreadFactory();
Thread thread = threadFactory.newThread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t i=" + i);
}
});
thread.start();
}
3.1.2newCachedThreadPool
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
// 缓存线程池创建的个数是去不确定的,根据每个线程的总执行时间确定的,当一个线程执行完后会有60秒的存活时间
// 如果有新的任务需要执行就使用刚才执行完空闲的线程执行,如果没有空闲的线程则新启动一个线程,最多启动Integer.MAX_VALUE个线程
for (int i = 0; i < 500; i++) {
int finalI = i;
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t " + "i=" + finalI);
}
});
}
}
开启了500个任务,这里测试的缓冲池中有0~48共49个线程,说明其它任务共用了线程池中的线程。
3.1.3newFixedThreadPool
public class ThreadPoolTest {
public static void main(String[] args) {
// 固定线程池,线程池中的个数是固定的值
ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(() -> {
System.out.println(Thread.currentThread().getName());
});
executorService.execute(thread);
}
executorService.shutdown();
}
}
我的机器Runtime.getRuntime().availableProcessors()=4,所以有4个线程。
3.1.4newScheduledThreadPool
public static void main(String[] args) {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());
for (int i = 0; i < 10; i++) {
int finalI = i;
// 不会立即执行,5秒钟后执行
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\tloop i=" + i);
executorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t " + "i=" + finalI);
}
}, 5, TimeUnit.SECONDS);
}
}
3.1.5newSingleThreadScheduledExecutor
public static void main(String[] args) {
// 所有任务只使用一个线程在执行
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
for (int i = 0; i < 10; i++) {
int finalI = i;
scheduledExecutorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t " + "i=" + finalI);
}
});
}
scheduledExecutorService.shutdown();
}
只有一个线程在运行。
3.2Callable示例
public class ThreadPoolTest {
public static void main(String[] args) {
List<Future<String>> futureList = new ArrayList<>();
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
Future<String> future = executorService.submit(new MyCallable(i));
futureList.add(future);
}
futureList.forEach(future -> {
while (!future.isDone()) { }
try {
String result = future.get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} finally {
// 执行以前提交的任务,但不接受新任务
executorService.shutdown();
}
});
}
}
class MyCallable implements Callable<String> {
private int i;
public MyCallable(int i){
this.i = i;
}
@Override
public String call() throws Exception {
return Thread.currentThread().getName() + "\t" + "i=" + i;
}
}
3.3自定义线程池
ThreadPoolExecutor相关源码
public class ThreadPoolExecutor extends AbstractExecutorService {
/*
*
* @param corePoolSize 核心线程池: 线程池中所保存的核心线程数,包括空闲线程
* @param maximumPoolSize 最大线程池 池中允许的最大线程数。
* @param keepAliveTime 线程池中的空闲线程所能持续的最长时间
* @param unit keepAliveTime对应的单位
* @param workQueue 任务执行前保存任务的队列,仅保存由 execute方法提交的Runnable任务
*
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory);
/*
* 当创建一个线程池的时候,该线程池会包含两个线程,一个是核心线程池,一个是最大线程池。
* 当提交一个任务的时候,如果核心线程池没有满的情况下就在核心线程池中执行
* 如果核心线程池已经满了,就把该线程放到队列中,如果核心线程池有空闲的线程就从队列中取出来执行
* 当队列满的情况下(每种队列都会设置最大的容量),就把线程放到最大线程池中去执行
* 当最大线程池也已经满的情况下就走拒绝策略
*/
public void execute(Runnable command) {
if (command == null) throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
}
测试
public static void main(String[] args) {
// 等待队列
BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(20);
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 500, TimeUnit.MICROSECONDS, blockingQueue);
for (int i = 0; i < 10; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t running...");
try { Thread.sleep(500); } catch (InterruptedException e) { }
});
}
threadPool.shutdown();
}
4.总结
---------------------------------------------------------------------end--------------------------------------------------------------
参考博文:https://blog.csdn.net/vbirdbest/article/details/81455951