多线程技术讲解
传统线程技术的回顾
1. 多线程的创建(两种)
- 通过创建线程对象,复写run(),通过start()调用该线程。
本质是继承
Thread thread = new Thread(){
@Override
public void run() {...}
};
thread.start();
- 通过有参构造创建线程对象,复写run(),通过start()调用该线程。
本质是接口实现Runnable
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {...}
});
thread2.start();
- 源码
- 首先进入构造方法中看见init方法
通过对成员变量的target的赋值,就能实现多线程。
2. 线程池的创立
传统的线程创建,寿命只有从线程对象的确定,以及到线程代码的结束,如果一个程序需要不同的时间,创建大量不同的线程,用上述的两种方式创建线程,太过于消耗内存资源。由此引入新的的技术
- 线程池技术
线程池的创立都是通过Executors创建,通过代理实现不同类型线程池的创建,
线程池都是通过不同的队列的实现,(LinkedBlockingQueue,SynchronousQueue)
// 固定线程数
ExecutorService threadPool = Executors.newFixedThreadPool(3);
// 根据任务调度线程数(首选,当它回收旧线程时,停止创建新线程,在使用出问题时,才使用newFixedThreadPool)
ExecutorService threadPool = Executors.newCachedThreadPool();
// 单线程池像是newFixedThreadPool(1);在处理单线程同步问题的时候,可以减少对同步复杂性,因为只有一个线程执行,就不存在同步的可能
ExecutorService threadPool = Executors.newSingleThreadExecutor();
常用的API
- execute(Runnable command ) 线程调用线程池资源的核心
- Thread.CurrentThread()获得当前线程对象
- shutdownNow()
- shutdown();
例子
ExecutorService threadPool = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int task = i;
threadPool.execute(new Runnable() {
@Override
public void run() { }
}
});
}
System.out.println("shutdown");
threadPool.shutdownNow();
2.1 从任务中的返回值
- Runnable是执行工作的独立任务,但是它不返回值,如希望在任务完成时能返回一个值,那么可以实现Callable接口而不是Runnable接口。Callable是一种具有类型参数的泛型,它的类型参数表示的是从call()而不是run()中返回的值,并且使用ExecutorService.submit()方法调用它
class TaskWithResult implements Callable<String>{
private int id;
public TaskWithResult(int id){
this.id = id;
}
public String call(){
return "result of TaskWithResult"+id;
}
}
public class CallableDemo{
public static void main(String[] args){
ExecutorService exec = Executors.newCacheThreadPool();
List<Future<String>> result = new ArrayList<>();
for(int i = 0;i<10;i++){
result.add(exec.submit(new TaskWithResult(1)));
}
for(Future<String> fs : result){
try{
System.out.println(fs.get());
}catch(Exception e){
e.printStackTrace();
}finally{
exec.shutdown();
}
}
}
}
- submit()方法会产生Future对象,它用Callable返回结果的特定类型进行了参数化,可以用isDone()查询Future是否完成,若完成,那么就有一个结果,通过get()获取,若未完成,直接用get()方法获取,则会阻塞,直至结果就绪。 一般先用isDone()判断是否完成,再用get