之前我们已经学习过了线程,线程的创建方式我们讲了7种,感兴趣的小伙伴可以去看看前面的讲解。线程的创建
那么我们学习了那么多关于线程的知识,为什么还要再用到线程池呢?那么我们就要来思考一下,线程有什么问题吗?
因此引出了线程池~~
线程池是使用池化技术管理和使用线程的一种机制。
池化技术:提前准备一些资源,在需要时可以重复使用使用提前准备的资源。
池化技术的应用也比较多:
线程池相比线程来说,它不需要频繁的创建和销毁线程,而是提前将线程创建好休眠在线程池中,有任务需要它时再唤醒它,线程一旦创建就不会销毁,会放入线程池中,如下图:
因此,他有以下几个优点:
线程池的实现总的来说有两类:
线程池的创建方式总共包含以下7种(其中6种是通过Executors 创建的,1种是通过ThreadPoolExecutor创建的):
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 创建一个固定大小的线程池
*/
public class ThreadPoolDemo1 {
public static void main(String[] args) {
//1.创建了一个包含了5个线程的线程池
ExecutorService threadPool= Executors.newFixedThreadPool(5);
//2,使用线程池执行任务二(给线程池添加任务)
for (int i = 0; i < 10; i++) {
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程名称:"+Thread.currentThread().getName());
}
});
}
}
}
public class ThreadPoolDemo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//1.创建了一个包含了5个线程的线程池
ExecutorService threadPool= Executors.newFixedThreadPool(5);
//2,使用线程池执行任务(给线程池添加任务)
for (int i = 0; i < 5; i++) {
Future<Integer> result = threadPool.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int num=new Random().nextInt(9);
System.out.println("随机数:"+num);
return num;
}
});
System.out.println("线程池的返回结果:"+result.get());
}
}
}
观察上面这两段代码,我们可以发现,向线程池中添加任务的方式有两种:
在上面的执行中,可以看到没有设置线程的名称时,它就会是一个默认名 ,线程工厂可以让我们自定义线程名称或优先级
下面来看一下线程工厂的实现吧~
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
/**
* 线程工厂的演示
*/
public class ThreadPoolDemo3 {
public static void main(String[] args) {
//1.创建线程工厂
ThreadFactory threadFactory=new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread=new Thread(r);
//设置线程命名规则
thread.setName("我的线程-"+r.hashCode());
//设置线程优先级
thread.setPriority(Thread.MAX_PRIORITY);
return thread;
}
};
ExecutorService threadPool = Executors.newFixedThreadPool(5, threadFactory);
for(int i=0;i<5;i++){
threadPool.submit(()->{
//任务
Thread thread = Thread.currentThread();
System.out.println("线程池开始执行了:"+thread.getName()+" "+thread.getPriority());
});
}
}
}
在上面的代码中我们设置了线程的名称和优先级,看看他会生效吗?
答案是肯定的,但是要注意的是我们一定要将任务传入这个线程工厂,否则就会出错。
如果我们什么都不传入,就会是什么都不打印。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 带缓存的线程池
*/
public class ThreadPoolDemo4 {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newCachedThreadPool();
for(int i=0; i<1000; i++){
int finalI=i;
threadPool.submit(()->{
System.out.println("i:"+finalI+"|线程名称:"+Thread.currentThread().getName());
});
}
}
}
有1000个任务并不会创建1000个线程,通过执行可以发现最多会创建100多个线程,然后进行复用。
import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 创建执行定时任务的线程池
*/
public class ThreadPoolDemo5 {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
System.out.println("添加任务的时间:"+ LocalDateTime.now());
scheduleTest(service);//只执行一次的定时任务
/**
* 只执行一次的定时任务
* @param service
*/
private static void scheduleTest(ScheduledExecutorService service) {
//执行定时任务(延迟3秒执行)
service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("执行了任务:"+LocalDateTime.now());
}
},3, TimeUnit.SECONDS);
}
}
这是延迟执行任务,他只能执行一次。
创建这个线程池时有3个参数:
我们可以让线程以以固定频率间隔n秒执行,创建这个线程池有四个参数:
import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 创建执行定时任务的线程池
*/
public class ThreadPoolDemo5 {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
System.out.println("添加任务的时间:"+ LocalDateTime.now());
//2S之后开始执行定时任务,定时任务每4s执行一次
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("执行任务:"+LocalDateTime.now());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},2,4,TimeUnit.SECONDS);
}
}
import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 创建执行定时任务的线程池
*/
public class ThreadPoolDemo5 {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
System.out.println("添加任务的时间:"+ LocalDateTime.now());
// //2S之后开始执行定时任务,定时任务每4s执行一次
// service.scheduleAtFixedRate(new Runnable() {
// @Override
// public void run() {
// System.out.println("执行任务:"+LocalDateTime.now());
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
// },2,4,TimeUnit.SECONDS);
//2s之后开始执行,每次执行间隔4s
service.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
System.out.println("执行任务:"+LocalDateTime.now());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},2,4,TimeUnit.SECONDS);
}
}
看出这两个方法的区别了吗?
scheduleAtFixedRate是以上一次任务开始的时间,加上任务的执行周期,作为下一次定时任务的开始时间。
scheduleWithFixedDelay是以上一次任务的结束时间,加上任务的执行周期,作为下次定时任务的开始时间。
注意:
在scheduleAtFixedRate中,有时任务的执行时间大于延迟任务设定的时间间隔,那么当任务执行完之后才会开始执行下次任务,此时并不是以设定执行周期来执行任务。
ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
System.out.println("添加任务的时间:"+ LocalDateTime.now());
// scheduleTest(service);//只执行一次的定时任务
//2S之后开始执行定时任务,定时任务每4s执行一次
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("执行任务:"+LocalDateTime.now());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},2,2,TimeUnit.SECONDS);
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 单个线程的线程池
*/
public class ThreadPoolDemo7 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
int finalI=i;
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("任务名:"+finalI+"线程名:"+Thread.currentThread().getName());
}
});
}
}
}
那么肯定有同学有疑问了,单线程的线程池与单线程有什么区别呢?他有什么作用呢?
import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 创建单线程的执行定时任务的线程池
*/
public class ThreadPoolDemo6 {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
System.out.println("执行任务的时间:"+LocalDateTime.now());
service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("执行任务:"+ LocalDateTime.now());
}
},2, TimeUnit.SECONDS);
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 根据当前设备的配置自动生成线程池
*/
public class ThreadPoolDemo8 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newWorkStealingPool();
for (int i = 0; i < 100; i++) {
int finalI=i;
executorService.submit(()->{
System.out.println("任务:"+finalI+"线程名:"+Thread.currentThread().getName());
});
}
while (!executorService.isTerminated()){};
}
}
具体实现:
import java.util.concurrent.*;
/**
* 手动方式创建线程池
*/
public class ThreadPoolDemo10 {
public static void main(String[] args) {
ThreadFactory factory=new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread=new Thread(r);
return thread;
}
};
ThreadPoolExecutor executor=new ThreadPoolExecutor(2,5,10,
TimeUnit.SECONDS,new LinkedBlockingDeque<>(2),factory,new ThreadPoolExecutor.DiscardPolicy());
for (int i = 1; i < 6; i++) {
int finalI=i;
executor.submit(()->{
try {
Thread.sleep(10*finalI);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务:"+finalI);
});
}
}
}
线程池的拒绝策略共有5种(4种JDK提供的+1种自定义拒绝策略):
1.AbortPolicy()
提示异常拒绝执行,是默认的拒绝策略:
import java.util.concurrent.*;
/**
* 手动方式创建线程池
*/
public class ThreadPoolDemo10 {
public static void main(String[] args) {
ThreadFactory factory=new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread=new Thread(r);
return thread;
}
};
ThreadPoolExecutor executor=new ThreadPoolExecutor(2,2,10,
TimeUnit.SECONDS,new LinkedBlockingDeque<>(2),factory,new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 5; i++) {
int finalI=i;
executor.submit(()->{
try {
Thread.sleep(10*finalI);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务:"+finalI);
});
}
}
}
丢弃任务但不抛出异常。如果线程数大于最大核心线程数,则后续提及的任务都会被丢弃,且是静默丢弃。
import java.util.concurrent.*;
/**
* 手动方式创建线程池
*/
public class ThreadPoolDemo10 {
public static void main(String[] args) {
ThreadFactory factory=new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread=new Thread(r);
return thread;
}
};
ThreadPoolExecutor executor=new ThreadPoolExecutor(2,2,10,
TimeUnit.SECONDS,new LinkedBlockingDeque<>(2),factory,new ThreadPoolExecutor.DiscardPolicy());
for (int i = 0; i < 5; i++) {
int finalI=i;
executor.submit(()->{
try {
Thread.sleep(10*finalI);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务:"+finalI);
});
}
}
}
3.DiscardOldestPolicy()
丢弃阻塞队列最前面的任务,然后重新提交被拒绝的任务。
import java.util.concurrent.*;
/**
* 手动方式创建线程池
*/
public class ThreadPoolDemo10 {
public static void main(String[] args) {
ThreadFactory factory=new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread=new Thread(r);
return thread;
}
};
ThreadPoolExecutor executor=new ThreadPoolExecutor(2,2,10,
TimeUnit.SECONDS,new LinkedBlockingDeque<>(2),factory,new ThreadPoolExecutor.DiscardOldestPolicy());
for (int i = 0; i < 5; i++) {
int finalI=i;
executor.submit(()->{
try {
Thread.sleep(10*finalI);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务:"+finalI);
});
}
}
}
4.CallerRunsPolicy()
如果任务被拒绝了,则由调用线程(提交任务的线程)直接执行此任务。
import java.util.concurrent.*;
/**
* 手动方式创建线程池
*/
public class ThreadPoolDemo10 {
public static void main(String[] args) {
ThreadFactory factory=new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread=new Thread(r);
return thread;
}
};
ThreadPoolExecutor executor=new ThreadPoolExecutor(2,2,10,
TimeUnit.SECONDS,new LinkedBlockingDeque<>(2),factory,new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 0; i < 5; i++) {
int finalI=i;
executor.submit(()->{
try {
Thread.sleep(10*finalI);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务:"+finalI);
});
}
}
}
5.自定义拒绝策略
除了上面四种JDK提供的拒绝策略外,我们还可以自定义拒绝策略,具体实现:
import java.util.concurrent.*;
/**
* 手动方式创建线程池
*/
public class ThreadPoolDemo10 {
public static void main(String[] args) {
ThreadFactory factory=new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread=new Thread(r);
return thread;
}
};
//自定义拒绝策略
ThreadPoolExecutor executor=new ThreadPoolExecutor(2, 2, 10,
TimeUnit.SECONDS, new LinkedBlockingDeque<>(2), factory, new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("我执行了自定义拒绝策略");
}
});
for (int i = 0; i < 5; i++) {
int finalI1 = i;
executor.submit(()->{
try {
Thread.sleep(100*finalI1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务名:"+Thread.currentThread().getName());
});
}
}
}