最近项目组组织学习线程池,也受到了打击,以后坚持做笔记不偷懒了。
要了解线程池先了解几个相关的概念
线程:进程中负责执行的单元
进程:进程有独立的代码和数据空间(一个app),一个进程可以有多个线程
多线程:解决多任务执行的需求,合理使用cpu的资源,线程的切换由cpu决定,因此那个线程在执行具有不确定性
线程池:思想就是开辟出一个对象池的思想,就是开辟出一块内存空间,存放很多未死的线程,池中线程的调度由池管理器来管理,当有线程任务时从池中取出线程执行,执行完成后线程对象放回池中,这样可以避免反复创建线程对象带来的性能开销,节约了系统资源。
创建线程的三种方式
1、继承Thread类,覆盖run()方法。创建线程对象并用start()方法启动线程。
2、实现Runnable接口来创建Thread线程 run();
3、通过实现Callable接口来创建Thread线程 call()
Thread 类中的 start() 和 run() 方法,start会重新启动线程
系统接口HandlerThread 继承了Thread,他是可以使用Handler的Thread,一个具有消息循环的线程。 run()方法中通过Looper.prepare()来创建消息队列,通过Looper.loop(),来开启消息循环,在run()
方法中执行耗时操作,Handler内部创建了一个消息队列,外部需要Handler的方式通知
HandlerThread执行具体的任务,通过quite()/quitSafely()方法来终止线程的执行。
在选择使用继承Thread还是实现Runnable时JAVA类可以多实现却不可以多继承根据情况做选择。
与Runnable的主要区别是 Callable 的 call() 方法可以返回值和抛出异常,而 Runnable 的 run() 方法没 有这些功能。Callable 可以返回装载有计算结果的 Future 对象。
Callable无法在新线程中new Thread(Runnable r)使用,Thread 类只支持 Runnable不过 Callable 可以 使用 ExecutorService 。
Callable创建线程的两种方式
1、通过实现Callable接口来创建Thread线程:
步骤1:创建实现Callable接口的类SomeCallable;
步骤2:创建一个类对象:
Callable oneCallable = new SomeCallable();
步骤3:由Callable创建一个FutureTask对象:
FutureTask oneTask = new FutureTask(oneCallable);
FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了Future和Runnable接口,他的run方法中实际上会调用oneCallable.call()。
步骤4:由FutureTask创建一个Thread对象:
Thread oneThread = new Thread(oneTask);
步骤5:启动线程:
oneThread.start();
事例:
Callable callable =newCallable() {
@Override
publicStringcall() throws Exception {
return"哈哈哈";
}};
FutureTask task =newFutureTask(callable);
Thread t =newThread(task);t.start();// 启动线程t
ask.cancel(true);// 取消线程
2、通过线程池来创建线程:
步骤1:创建线程池:
ExecutorService pool = Executors.newCachedThreadPool();
步骤2:通过Runnable对象或Callable对象将任务提交给ExecutorService对象:
Future future = pool.submit(new Callable() );。
Future
该接口定义5个方法
boolean cancel(boolean mayInterruptIfRunning):试图取消对此任务的执行。如果任务已完成、或已取消,或者由于某些其他原因而无法取消,则此尝试将失败。当调用 cancel() 时,如果调用成功,而此任务尚未启动,则此任务将永不运行。如果任务已经启动,则 mayInterruptIfRunning 参数确定是否应该以试图停止任务的方式来中断执行此任务的线程。此方法返回后,对 isDone() 的后续调用将始终返回 true。如果此方法返回 true,则对 isCancelled() 的后续调用将始终返回 true。
boolean isCancelled():如果在任务正常完成前将其取消,则返回 true。
boolean isDone():如果任务已完成,则返回 true。 可能由于正常终止、异常或取消而完成,在所有这些情况中,此方法都将返回 true。
V get()throws InterruptedException,ExecutionException:如有必要,等待计算完成,然后获取其结果。
V get(long timeout,TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException: 如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)
FutureTask
FutureTask实现了两个接口,Runnable和Future,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
public classFutureTaskimplementsRunnableFuture {
.........
}
run()方法调用了callable中的call()
importjava.util.concurrent.*;
publicclassTest {
public static void main(String[] args) throwsInterruptedException,
ExecutionException {
finalExecutorService exec = Executors.newFixedThreadPool(5);
Callable call =newCallable() {
publicString call()throwsException {
Thread.sleep(1000*10);//休眠指定的时间,此处表示该操作比较耗时
return"Other less important but longtime things.";
}
};
Future task = exec.submit(call);
//重要的事情
System.out.println("Let's do important things. start");
Thread.sleep(1000*3);
System.out.println("Let's do important things. end");
//不重要的事情
while(! task.isDone()){
System.out.println("still waiting....");
Thread.sleep(1000*1);
}
System.out.println("get sth....");
String obj = task.get();
System.out.println(obj);
//关闭线程池
exec.shutdown();
}
}
输出结果:
Let's do important things. start
Let's do important things. end
still waiting....
still waiting....
still waiting....
still waiting....
still waiting....
still waiting....
still waiting....
get sth....
Other less important but longtime things.
多线程的特点
1)适当的提高程序的执行效率(多个线程同时执行)。
2)适当的提高了资源利用率(CPU、内存等)。
1)占用一定的内存空间。
2)线程越多CPU的调度开销越大。
3)程序的复杂度会上升。
synchronized
修饰一个代码块,修饰一个方法,修改一个静态的方法,修饰一个类。
wait(),notify(),notifyAll(),调用这三个方法中任意一个,当前线程必须是锁的持有者,如果不是会抛出一个 IllegalMonitorStateException 异常。
wait() 方法使实体所处线程暂停执行,从而使对象进入等待状态,直到被 notify() 方法通知或者 wait() 的等待的时间到。
sleep() 方法使持有的线程暂停运行,从而使线程进入休眠状态,直到用 interrupt 方法来打断他的休眠或者 sleep 的休眠的时间到。
wait() 方法进入等待状态时会释放同步锁,而 sleep() 方法不会释放同步锁。
thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
Thread.yield():线程放弃运行,将CPU的控制权让出。
线程池
1)避免线程的创建和销毁带来的性能开销。
2)避免大量的线程间因互相抢占系统资源导致的阻塞现象。
3}能够对线程进行简单的管理并提供定时执行、间隔执行等功能。
Java里面线程池的顶级接口是 Executor,不过真正的线程池接口是 ExecutorService, ExecutorService 的默认实现是 ThreadPoolExecutor;
普通类 Executors 里面调用的就是 ThreadPoolExecutor。
publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory){
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler);}
1)corePoolSize:线程池的核心线程数,一般情况下不管有没有任务都会一直在线程池中一直存活,只有在 ThreadPoolExecutor 中的方法 allowCoreThreadTimeOut(boolean value) 设置为 true 时,闲置的核心线程会存在超时机制,如果在指定时间没有新任务来时,核心线程也会被终止,而这个时间间隔由第3个属性 keepAliveTime 指定。
2)maximumPoolSize:线程池所能容纳的最大线程数,当活动的线程数达到这个值后,后续的新任务将会被阻塞。
3)keepAliveTime:控制线程闲置时的超时时长,超过则终止该线程。一般情况下用于非核心线程,只有在 ThreadPoolExecutor 中的方法 allowCoreThreadTimeOut(boolean value) 设置为 true时,也作用于核心线程。
4)unit:用于指定 keepAliveTime 参数的时间单位,TimeUnit 是个 enum 枚举类型,常用的有:TimeUnit.HOURS(小时)、TimeUnit.MINUTES(分钟)、TimeUnit.SECONDS(秒) 和 TimeUnit.MILLISECONDS(毫秒)等。
5)workQueue:线程池的任务队列,通过线程池的 execute(Runnable command) 方法会将任务 Runnable 存储在队列中。
6)threadFactory:线程工厂,它是一个接口,用来为线程池创建新线程的。
Executors提供四种线程池
newCachedThreadPool 可变尺寸的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute() 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。注意,可以使用 ThreadPoolExecutor 构造方法创建具有类似属性但细节不同(例如超时参数)的线程池。(只有非核心线程,最大线程数非常大,所有线程都活动时,会为新任务创建新线程,否则利用空闲线程(60s空闲时间,过了就会被回收,所以线程池中有0个线程的可能)处理任务)
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,所有的任务是串行执行的,如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它,保证所有任务按照指定顺序(FIFO)执行。(只有一个核心线程,确保所有任务都在同一线程中按顺序完成。因此不需要处理线程同步的问题。)
newFixedThreadPool 固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小,线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。(只有核心线程,并且数量固定的,也不会被回收,所有线程都活动时,因为队列没有限制大小,新任务会等待执行。)
newScheduledThreadPool 创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的需求。
线程池 submit 和 execute
接收的参数不一样
submit有返回值,而execute没有
线程池的关闭
ThreadPoolExecutor 提供了两个方法,用于线程池的关闭。
shutdown():不会立即的终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务。
shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务。