线程Thread是一个重量级资源,线程的创建、启动以及销毁都是比较耗费系统资源的,同时受限于系统资源的限制,线程的数量与系统性能是一种抛物线的关系,因此对线程的管理,是一种非常好的程序设计习惯,自JDK1.5起,utils包提供了ExecutorService[ɪɡˈzɛkjətɚ]线程池的实现。通俗的将:为了避免重复的创建线程,线程池的出现可以让线程进行复用。当有工作来,就会向线程池拿一个线程,当工作完成后,并不是直接关闭线程,而是将这个线程归还给线程池供其他任务使用。
一个线程池包括以下四个基本组成部分:
线程池作用就是限制系统中执行线程的数量。
根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。
Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。
其中AtomicInteger变量ctl的功能非常强大:利用低29位表示线程池中线程数,通过高3位表示线程池的运行状态:
1、RUNNING:-1 << COUNT_BITS,即高3位为111,该状态的线程池会接收新任务,并处理阻塞队列中的任务;
2、SHUTDOWN: 0 << COUNT_BITS,即高3位为000,该状态的线程池不会接收新任务,但会处理阻塞队列中的任务;
3、STOP : 1 << COUNT_BITS,即高3位为001,该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务;
4、TIDYING : 2 << COUNT_BITS,即高3位为010,该状态表示线程池对线程进行整理优化;
5、TERMINATED: 3 << COUNT_BITS,即高3位为011,该状态表示线程池停止工作;
首先创建一个自定义的线程,模拟并发任务。
public class MyThread extends Thread {
private int i;
public MyThread(int in) {
this.i = in;
}
public void run() {
try {
this.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(currentThread().getName()+"正在打印:"+i);
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyThreadText {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
executorService.execute(new MyThread(i));
}
executorService.shutdown();
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyThreadText {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
executorService.execute(new MyThread(i));
}
executorService.shutdown();
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyThreadText {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
executorService.execute(new MyThread(i));
}
executorService.shutdown();
}
}
为了体现其在指定时间内周期性的执行所提交的任务我们编写一个循环打印当前时间的线程
import java.text.SimpleDateFormat;
import java.util.Date;
public class ScheduledThread extends Thread {
private int i;
public ScheduledThread(int in) {
this.i = in;
}
@Override
public void run() {
while (true) {
try {
this.sleep(2000);
} catch (InterruptedException E) {
E.printStackTrace();
}
//SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
System.out.println(currentThread().getName()+"打印编号:"+i+"======>"+date);//答应当前时间
}
}
}
编写验证的主程序,设置线程池有5个线程可用,:
import java.util.concurrent.ScheduledThreadPoolExecutor;
public class ScheduledThreadTest {
public static void main(String[] args) {
ScheduledThreadPoolExecutor atpe = new ScheduledThreadPoolExecutor(5);//设置线程个数
for (int i = 0; i < 5; i++) {
atpe.execute(new ScheduledThread(i));//普通的提交方式,只提交一次,执行结束,线程不会退出。
}
}
}
通过实验我们发现,本实验例程需要创建的线程数应小于等于线程池的线程容量,否则线程不会回收。具体表现在,当 for (int i = 0; i < 5; i++) 修改为 for (int i = 0; i <10 i++) 以后,仍然只有前5个线程执行,因为线程循环执行,会一直占用线程池的资源。
为了验证这一猜想我们将程序修改如下
import java.util.Date;
public class ScheduledThread extends Thread {
private int i;
public ScheduledThread(int in) {
this.i = in;
}
@Override
public void run() {
Date date = new Date();
System.out.println(currentThread().getName()+"打印编号:"+i+"======>"+date);//答应当前时间
}
}
//主程序如下:
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ScheduledThreadTest {
public static void main(String[] args) {
ScheduledThreadPoolExecutor atpe = new ScheduledThreadPoolExecutor(4);//设置线程个数
for (int i = 0; i < 5; i++) {
atpe.execute(new ScheduledThread(i));
}
}
}
得到如下结果:
程序正常结束,且线程3被重复利用,并没达到线程池的最大容量4。
我们可以这样认为,newScheduledThreadPool这线程池可以使只执行一遍的线程以一定速率循环执行,但是如果以execute方式提交线程则不会重复执行。
我们对程序作出如下修改,使线程只执行一次:
import java.util.Date;
public class ScheduledThread extends Thread {
private int i;
public ScheduledThread(int in) {
this.i = in;
}
@Override
public void run() {
Date date = new Date();
System.out.println(currentThread().getName()+"打印编号:"+i+"======>"+date);//答应当前时间
}
}
同时主程序修改为:
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ScheduledThreadTest {
public static void main(String[] args) {
ScheduledThreadPoolExecutor atpe = new ScheduledThreadPoolExecutor(4);//设置线程个数
for (int i = 0; i < 5; i++) {
//参数1:initialDelay表示首次执行任务的延迟时间,参数2:period表示每次执行任务的间隔时间,参数3:TimeUnit.MILLISECONDS执行的时间间隔数值单位
atpe.scheduleAtFixedRate(new ScheduledThread(i),1000,2000,TimeUnit.MILLISECONDS);//以固定频率重复执行线程
}
}
}
可以得到类似的结果:
我们可以发现线程2实现了重复利用,虽然创建的线程是一次执行,但却实现了重复执行的效果,这就是该线程池最大的特点。
https://www.cnblogs.com/aspirant/p/6920418.html
https://www.cnblogs.com/superfj/p/7544971.html
https://www.cnblogs.com/zhaojinxin/p/6668247.html