创建线程的第三、四种方式
一、实现Callable接口
1.1与Runnable相比
与Runnable相比Callable功能更加强大
- 相比于run()方法,call()可以有返回值
- 方法可以抛出异常,被外面的操作捕获
- 支持范型的返回值
- 需要借助FutureTask类,比如获取返回结果
1.2实现步骤
①创建Callable接口实现类
Callable是一个范型接口
class Thread1 implements Callable {
}
②重写call方法
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100 ; i++) {
if (i % 2 == 0 ) {
System.out.println(i);
sum += i;
}
}
return sum;
}
③新建实现类对象
Thread1 thread1 = new Thread1();
④将实现类对象作为参数传递待FutureTask的构造器中,创建FutureTask的对象
FutureTask实现了Runnable接口,所以FutureTask的对象也是Runnable对象(实现了Runnable接口),可以交给Thread
FutureTask futureTask = new FutureTask(thread1);
⑤将FutureTask的对象传到Thread的构造器中,创建Thread类的对象并start()
//FutureTask也实现了Runnable接口
Thread t1 = new Thread(futureTask);
t1.start();
⑥获取Callable返回值 futureTask.get()
try {
//futureTask.get()获取的是FutureTask构造器参数Callable实现类重写的call()的返回值
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
二、使用线程池
2.1.1 背景
经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大
2.1.2 思路
提前创好多个线程,放在线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。
2.1.3 好处
①提高响应速度:减少了创建新线程的时间
②降低资源消耗:重复利用线程池中线程,不需要每次都创建
③便于线程管理
2.2 创建线程池
方式一、线程池工具类Executors调用方法
//Executors工具类、线程池的工厂类,用法语创建并返回不同类型的线程池
//1、创建一个可根据需要创建性线程的线程池
Executors.newCachedThreadPool();
//2、创建一个可重用固定线程数的线程池
Executors.newFixedThreadPool(n);
//3、创建一个只有一个线程的线程池
Executors.newSingleThreadExecutor();
//4、创建一个线程池,它可安排再给定延迟后运行命令或者定期地执行
应用:定时器
Executors.newScheduledThreadPool(n);
Executors的底层也是基于线程池的实现类ThreadPoolExecutor创建线程池对象
2.2.1 Executors.newFixedThreadPool(int n);
①创建指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
②新建Runnable或Callable接口实现类,并重写run方法或call方法(新建任务)
Runnable:
class PoolThread implements Runnable{
@Override
public void run() {
for (int i = 1; i <= 100 ; i++) {
if (i % 2 == 0 ) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
Callable
class PoolThread1 implements Callable {
@Override
public Object call() {
for (int i = 1; i <= 100 ; i++) {
if (i % 2 == 0 ) {
continue;
}
System.out.println(Thread.currentThread().getName()+":"+i);
}
return null;
}
}
③ 执行指定的线程操作需要提供实现类对象
excute适用于Runnable对象,submit适用于Callable对象 excute在Excutor中
Runnable
service.execute(new PoolThread());//适合适用于Runnable,参数只能是Runnable
Callable
service.submit(new FutureTask(new PoolThread1()));//返回一个FutureTask
④关闭连接池
service.shutdown();//等待全部任务执行完毕后关闭线程池
service.shutdownNow();//立即关闭线程池,可能造成任务丢失
运行结果
...
pool-1-thread-1:72
pool-1-thread-1:74
pool-1-thread-1:76
pool-1-thread-1:78
pool-1-thread-1:80
pool-1-thread-2:45
pool-1-thread-2:47
pool-1-thread-1:82
pool-1-thread-1:84
pool-1-thread-1:86
...
方式二、JDK5提供代表线程池的接口:ExcutorService
//构造器构造
public ThreadPoolExecutor(int corePoolSize, 参数说明: 线程池数量(核心线程)>0
int maximumPoolSize, 线程池可支持的最大数量 >=核心线程
long keepAliveTime, 指定临时线程的最大存活时间 >=0
TimeUnite unit, 指定存活时间的单位,秒,分,时,天 时间单位
BlockingQueue<Runnable> workQueue, 指定任务队列 不为 null
ThreadFactory threadFactory, 指定用来创建线程的线程工厂 不为 null
RejectExecutionHandler handler) 指定线程忙,任务满时,新任务来了的梳理方式 不为 null
2.3 管理线程池
2.3.1 设置线程池属性
//类型强转
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
service1.setCorePoolSize(15);
service1.setKeepAliveTime();