目标:掌握多线程的使用及应用场景
怎么使用多线程
// 尽量不要如此使用 new Thread(new Runnable() { @Override public void run() { } }).start(); // 通过线程池方便管理 Executor executor = Executors.newCachedThreadPool(); executor.execute(runnable);
进程和线程的区别
其实没有可比性,之所以大家喜欢用来比较是因为他们都可以并行,两个进程可以并行,两个线程也可以并行,所以大家喜欢用来对比。
但其实差别还是比较大的,进程在操作系统中是拥有独立空间的,并且进程间是不共享的,而一个进程中可能会包含很多个线程,而线程间是可以共享资源。
CPU线程与操作系统的线程怎么理解
CPU线程就是我们平时听到的几核CPU的概念,比如6核的CPU,它的CPU线程就是6,它表示CPU同时只能干六件事,而我们代码中所开的线程其实是操作系统的线程,操作系统通过时间分片可以将线程开的很多很多。
ThreadPoolExecutor
以ThreadPoolExecutor为例,他的构造函数里面会有几个重要的参数:
第一个参数corePoolSize表示该线程池需要保留的线程数量,即使在空闲的时候。
maximumPoolSize表示当前线程池最大可以拥有多少个线程,当线程池中开到最大,并且所有线程都在执行任务,此时如果有任务来了,就需要等待线程池中有空闲的线程释放出来。
keepAliveTime表示当线程池中超过corePoolSize的部分,在空闲状态时可以继续保留的时间,一旦超过这个时间就会销毁。
timeUnit表示上面参数的时间单位
workQueue显然就是线程池正在满池运行,新的待执行任务需要放在这个队列里等待空闲线程的到来。
newFixedThreadPool(int nThreads)
这个方法是用来创在保持特定数量的线程池,因为这个数量是既是线程池的最大线程数,也是线程池需要保留的线程数,所以,线程也不好增加,在空闲状态时也不能回收多余的。
所以它的应用场景也是比较少的,一般是用来集中处理一些爆发性的事物后立即释放,比如突然来了20张图片要处理,这个时候肯定不能一张一张的处理,可以开多线程并行处理,如下所示:
List<Bitmap> imgs = somewhereGetImgs(20); ExecutorService executor = Executors.newFixedThreadPool(20); for(Bitmap bitmap: imgs) { // 向线程池中扔了20个任务同时执行 executor.execute(processImgRunnable); } // 执行完就快速的回收 executor.shutdown();
Callable
有返回值的Runnable。
举个例子:
既然要放到异步线程里去执行,那返回结果又有什么意义呢,难道要阻塞主线程一直等任务线程执行完?
上面的例子就说明了部分使用场景,我们可以先拿到一个future的实例来在后面进行判断是否任务执行完了(当然,不一定是上面死循环的方式),实际可以根据具体业务来做。