1、线程池理解
在程序中,创建一个线程,需要分配给该线程内存,还要进行系统调用,频繁的创建和销毁线程,会大大降低系统的运行效率,线程池的作用在于,线程可以不用频繁创建和销毁,可以一直复用,提升系统性能和和进行线程管理;
2、jdk提供几种线程池使用
(1)缓冲线程池,使用例子如下:
public class NewCachedThreadPoolDemo {
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newCachedThreadPool();
for (int i = 0; i < 7; i++) {
final int a = i;
singleThreadExecutor.execute(() -> {
System.out.println("线程执行:" + a);
});
}
// 线程池销毁
singleThreadExecutor.shutdown();
}
}
该线程池有个弊端,就是最大线程数默认是Integer.MAX_VALUE,即是0x7fffffff,线程可以默认无限扩大,采用SynchronousQueue装等待的任务,这个阻塞队列没有存储空间,这意味着只要有请求到来,就必须要找到一条工作线程处理他,如果当前没有空闲的线程,那么就会再创建一条新的线程;
(2)固定线程池,使用例子如下:
public class NewFixedThreadPoolDemo {
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 7; i++) {
final int a = i;
singleThreadExecutor.execute(() -> {
System.out.println("线程执行:" + a);
});
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 线程池销毁
singleThreadExecutor.shutdown();
}
}
该线程池有个弊端,就是队列是使用了LinkedBlockingQueue,该队列可以无限堆积线程任务,导致系统任务越来越大;
(3)单个线程池,使用例子如下:
public class NewSingleThreadExecutorDemo {
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
// 批量添加线程
for (int i = 0; i < 5; i++) {
final int a = i;
singleThreadExecutor.execute(() -> {
System.out.println("线程执行:" + a);
});
}
singleThreadExecutor.shutdown();
}
}
该线程池有个弊端,就是队列是使用了LinkedBlockingQueue,该队列可以无限堆积线程任务,导致系统任务越来越大; 备注:如果线程池异常的话,线程会重新创建一个线程,重新使用
(4)调度线程池,使用例子如下:
public class ScheduledThreadPoolDemo {
public static void main(String[] args) {
ScheduledExecutorService ses = Executors.newScheduledThreadPool(3);
System.out.println("添加任务的时间:" + LocalDateTime.now());
ses.schedule(() -> System.out.println("执行子任务:" + LocalDateTime.now()), 3, TimeUnit.SECONDS);
}
}
可以定时执行任务
3、线程池的执行原理
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler);
线程池的几个参数:
corePoolSize:核心线程数
maximumPoolSize:最大线程数
keepAliveTime:空闲线程存活时间
TimeUnit:时间单位
BlockingQueue:线程池任务队列
ThreadFactory:创建线程的工厂
RejectedExecutionHandler:拒绝策略
当任务来时,当线程池中的线程数量小于核心线程数时,就会新建一个线程执行该任务,当该线程数打到核心线程数时,就会把任务放到队列中,当队列满了,就会当线程数量小于创建最大线程数时,就会创建线程去执行任务,当队列满了,并且最大线程数超过了定义的数量,就会触发拒绝机制;
4、 线程池的拒绝策略
拒绝策略:当线程池的任务超出线程池队列可以存储的最大值之后,执行的策略。默认的拒绝策略有以下 4 种:
AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
CallerRunsPolicy:使用当前调用的线程来执行此任务。
DiscardOldestPolicy:丢弃队列头部(最旧)的一个任务,并重新执行当前任务(重复此过程)。
DiscardPolicy:也是丢弃任务,但是不抛出异常。
备注:RejectedExecutionHandler是一个接口类,也可以自己实现该类中的方法,自己定义拒绝策略,默认的拒绝策略是直接抛出异常;
5、 线程池关闭中方法区别
(1)shutdown():该关闭会等线程池中的任务执行完毕,再关闭线程池;
(2)shutdownNow():该关闭会立即关闭线程池,不等方法执行完毕;
6、 线程池提交任务:
(1)execute():该方法是一个Runnable类;
(2)submit():该方法可以是一个Runnable类,也可以是一个Callable类,Callable可以有返回值,可以使用 Future接收结果:
public class ThreadPool1Demo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<String> future = executorService.submit(() -> {
System.out.println(Thread.currentThread().getName() + ":哈哈");
return "success";
});
try {
String result = future.get();
System.out.println("结果为:" + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
executorService.shutdown();
}
}
备注:Future还可以捕获submit抛出的异常结果
public class ThreadPool1Demo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<String> future = executorService.submit(() -> {
System.out.println(Thread.currentThread().getName() + ":哈哈");
int a = 1 / 0;
return "success";
});
// try {
// String result = future.get();
// System.out.println("结果为:" + result);
// } catch (InterruptedException | ExecutionException e) {
// e.printStackTrace();
// }
executorService.shutdown();
}
}
7、 自定义线程池
在系统开发中,一般不适用jdk默认的几种线程池,因为存在弊端,阿里巴巴开发手册也不推荐,所以一般使用自定义线程池:
public class ThreadPoolExecutor1Demo {
public static void main(String[] args) {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 20,
0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024));
threadPool.execute(()->{
System.out.println(12);
});
threadPool.shutdown();
}
}
8、线程工厂
线程工厂,即ThreadFactory,用来创建线程,新线程是使用ThreadFactory创建的,如果没有指定,是使用默认的线程工厂
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadFactoryDemo {
public static void main(String[] args) {
ThreadFactory threadFactory = new ThreadFactory() {
AtomicInteger atomicInteger = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, "aa->" + atomicInteger.getAndIncrement());
return thread;
}
};
ExecutorService executorService = Executors.newFixedThreadPool(10, threadFactory);
executorService.submit(() -> {
System.out.println("线程池名称为:" + Thread.currentThread().getName());
System.out.println("hello world");
});
executorService.shutdown();
}
}
9、总结
总之,一般在使用线程池的使用,都是自定义线程池,而且线程池也是要合理的运用,才能提高系统效率,根据自己的开发系统代码定义;