说明:
在开发的过程当中,我们不会自己一个一个去造线程,效率太低,开发中用的是线程池
场景: 手机页面是在主线程中加载文本,图片的加载是开辟分线程做的,一个页面会显示很多图片,就意味着要开很多分线程
造线程要花时间,造完线程下载也要花时间,滑动页面时,甚至还没有造好就滑过去了(相当于白造了),所以在滑的过程中CPU,内存消耗比较多
经常创建和销毁、使用量特别大的资源,比如并发情况下的线程, 对性能影响很大。
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用
好处:
- 提高响应速度(减少了创建新线程的时间)
- 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
- 便于线程管理 :自己造的线程不便于管理
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止
还有其他属性
通过对属性的设置便于对线程更好的进行管理
注意:
放到线程池中的线程只是用完之后还回来了,并不是说线程就被销毁了
用完的线程没有销毁,而是放到线程池中等待重新被利用,降低资源消耗
JDK 5.0起提供了线程池相关API:ExecutorService 和 Executors
ExecutorService:真正的线程池接口,常见子类ThreadPoolExecutor
- void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行 Runnable
- Future submit(Callable task):执行任务,有返回值,一般又来执行 Callable
- void shutdown() :关闭连接池
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
- Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
- Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池
- Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池
- Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运 行命令或者定期地执行。
步骤:
- 1.提供指定数量的线程池,用ExecutorService进行接收,用Executors.创建并返回相应的线程池
- 2.提供实现Runnable或者Callable接口的类的对象
- 3.execute方法适用于实现Runnable接口的
- 4.submit可以去传一个Callable接口的,注意进行接收FutureTask futuretask=submit(Callable callable),因为返回值为Future
- 然后用futuretask.get()获取方法的结果
- 5.关闭连接池
Executors是一个工具类,有很多静态方法
ExecutorService是一个接口,指向的是接口实现类的对象,涉及到多态
这个接口有两个典型的方法,一个叫submit,一个叫execute(参数为Runnable),因为Runnable是重写run方法,run方法又没有返回值,所以execute没有返回值。
对于submit,适合使用Callable,因为参数就是Callable,返回的是Future,execute则适合使用Runnable
package test0;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
//ExecutorService是接口,就算有属性也是常量,是改不了的,所以要通过接口的实现类来设置线程池的属性
public class ThreadDemo {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);//按Alt+enter创建相应的对象变量,这里只是说造一个线程池,有这么多线程,但干什么事还要自己指定
//注意,如果想要设置线程池的属性,要有了线程池之后才可以,所以先要得到service,如果直接Executor.是不现实的
//怎么知道接口的实现类是谁?用Object中的getClass方法,用来获取对象到底是哪个类
System.out.println(service.getClass());//结果为类ThreadPoolExecutor
//那么其实可以考虑进行强制类型转换
ThreadPoolExecutor service1=(ThreadPoolExecutor)service;
//通过service1.set之类的进行属性的设置
service1.setCorePoolSize(15);
service.execute(new numThread());//会执行传入参数的run方法,是线程池中的一个线程做的
//执行完之后,如果线程池不用了,可以把线程池关闭
//service.shutdown();
//如果还用,并且想让另一个线程作别的事,可以再去造一个实现Runnable接口的实现类,比如numThread1
service.execute(new numThread1());//并不是说上面那个线程执行完之后才会做这个线程
service.shutdown();//程序也就停止了
}
}
class numThread implements Runnable{
@Override
public void run() {
for(int i=0;i<=100;i++){
if(i%2==0){
System.out.println(Thread.currentThread().getName()+":"+i);//pool-1-thread-1,表示的是池子里面的线程1
}
}
}
}
class numThread1 implements Runnable{
@Override
public void run() {
for(int i=0;i<=100;i++){
if(i%2!=0){
System.out.println(Thread.currentThread().getName()+":"+i);//pool-1-thread-1,表示的是池子里面的线程1
}
}
}
}