参考:
java&android线程池-Executor框架之ThreadPoolExcutor&ScheduledThreadPoolExecutor浅析(多线程编程之三)
并行和并发区别
1、并行是指两者同时执行一件事,比如赛跑,两个人都在不停的往前跑;
2、并发是指资源有限的情况下,两者交替轮流使用资源,比如一段路(单核CPU资源)同时只能过一个人,A走一段后,让给B,B用完继续给A ,交替使用,目的是提高效率
new Thread的弊端如下:
a. 每次new Thread新建对象性能差。
b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
c. 缺乏更多功能,如定时执行、定期执行、线程中断。
相比new Thread,Java提供的四种线程池的好处在于:
a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能。
线程池的作用:
线程池作用就是限制系统中执行线程的数量。
根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排 队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池 中有等待的工作线程,就可以开始运行了;否则进入等待队列。
为什么要用线程池:
1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。
ThreadPoolExecutor相关类的关系:
public interface Executor {}
//真正的线程池接口。
public interface ExecutorService extends Executor {}
//能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。
public interface ScheduledExecutorService extends ExecutorService {}
public abstract class AbstractExecutorService implements ExecutorService {}
//ExecutorService的默认实现。
public class ThreadPoolExecutor extends AbstractExecutorService {}
//继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。
public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {
private static class DelegatedExecutorService extends AbstractExecutorService {
private static class FinalizableDelegatedExecutorService extends DelegatedExecutorService {
ThreadPoolExecutor的构造方法:
public ThreadPoolExecutor(
int corePoolSize,//核心线程数,能够同时执行的任务数量;
int maximumPoolSize,//最大线程数,达到最大任务数新任务将会被阻塞;
long keepAliveTime,//线程没有任务执行时最多保持多久时间会终止,然后线程池的数目维持在corePoolSize 大小,在corePore<*<maxPoolSize情况下有用
TimeUnit unit,//时间单位;
BlockingQueue<Runnable> workQueue,//阻塞队列用来存储等待执行的任务,当前对线程的需求超过了corePoolSize大小才会放在这里
ThreadFactory threadFactory,//创建线程的工厂,使用系统默认的类,主要用来创建线程,比如可以指定线程的名字;
RejectedExecutionHandler handler) {//当任务数超过maximumPoolSize时,对任务的处理策略,默认策略是拒绝添加;
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
- 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
- 当调用 execute() 方法添加一个任务时,线程池会做如下判断:
- 如果正在运行的线程数量小于
corePoolSize
,那么马上创建线程运行这个任务;- 如果正在运行的线程数量大于或等于
corePoolSize
,那么将这个任务放入队列。- 如果这时候队列满了,而且正在运行的线程数量小于
maximumPoolSize
,那么还是要创建线程运行这个任务;- 如果队列满了,而且正在运行的线程数量大于或等于
maximumPoolSize
,那么线程池会抛出异常,告诉调用者“我不能再接受任务了”。
- 当一个线程完成任务时,它会从队列中取下一个任务来执行。
- 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于
corePoolSize
,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到corePoolSize
的大小。
这样的过程说明,并不是先加入任务就一定会先执行。
假设队列大小为 10,corePoolSize 为 3,maximumPoolSize 为 6,那么当加入 20 个任务时,执行的顺序就是这样的:首先执行任务 1、2、3,然后任务 4~13 被放入队列。这时候队列满了,任务 14、15、16 会被马上执行,而任务 17~20 则会抛出异常。最终顺序是:1、2、3、14、15、16、4、5、6、7、8、9、10、11、12、13。
处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,
如果三者都满了,使用handler处理被拒绝的任务。
一、currentSize < corePoolSize
空闲线程
新线程(优先)
二、corePoolSize < currentSize < maximumPoolSize
workQueue未满时,添加进来的任务会被安排到workQueue中等待执行。
workQueue已满时,会立即开启一个非核心线程来执行任务。
空闲线程(优先):超过一定的时间(keepAliveTime)时会被回收
新线程
三、currentSize > maximumPoolSize
调用handler默认抛出RejectExecutionExpection异常。
FixedThreadPool
是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。
但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
//线程池中均为核心线程,最多线程数nThreads
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
//单向链表实现的阻塞队列,先进先出的顺序,可无限排队
threadFactory);
}
CachedThreadPool
特点就是在线程池空闲时,即线程池中没有可运行任务时,它会释放工作线程,从而释放工作线程所占用的资源。
但是,但当出现新任务时,又要创建一新的工作线程,又要一定的系统开销。
并且,在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
//线程池中均为非核心线程,而且可无限创建线程
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
//如果现有线程无法接收任务,将会创建新线程来执行.
}
参考:Java线程池ThreadPoolExecutor详解
四种线程池:
//Java通过Executors提供四种线程池,分别为:
//newCachedThreadPool创建一个可缓存线程池,如果超过存活时间不活动,其会自动被终止。最大线程数不限
new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>())
//newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>())
//newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。最大线程数不限
super(corePoolSize, Integer.MAX_VALUE , DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue())
//newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())
队列
// 队列:先进先出
public interface BlockingQueue<E> extends Queue<E> {
public interface TransferQueue<E> extends BlockingQueue<E> {
public abstract class AbstractQueue<E> extends AbstractCollection<E> implements Queue<E> {
/*阻塞队列,线程安全*/
public class SynchronousQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, Serializable {
//一个不存储元素的阻塞队列。
}
public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, Serializable {
//一个由数组结构组成的有界阻塞队列。
}
public class PriorityBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, Serializable {
//一个支持优先级排序的无界阻塞队列。
}
public class LinkedBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, Serializable {
//一个由链表结构组成的有界阻塞队列。
}
public class LinkedTransferQueue<E> extends AbstractQueue<E> implements TransferQueue<E>, Serializable {
//一个由链表结构组成的无界阻塞队列。
}
public class LinkedBlockingDeque<E> extends AbstractQueue<E> implements BlockingDeque<E>, Serializable {
//一个由链表结构组成的双向阻塞队列
}
public class DelayQueue<E extends Delayed> extends AbstractQueue<E> implements BlockingQueue<E> {
//一个使用优先级队列实现的无界阻塞队列
}
/*非阻塞队列*/
public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> implements Queue<E>, Serializable {
//并发队列,基于链接节点的、无界的、线程安全。
}
线程池提交之execute和submit区别:
向线程池提交任务有两种方式:
Executor.execute(Runnable command);
ExecutorService.submit(Callable<T> task);
1.对返回值的处理不同
execute方法不关心返回值。
submit方法有返回值,Future.
2.对异常的处理不同
excute方法会抛出异常。
sumbit方法不会抛出异常。除非你调用Future.get()
线程池工具类:
/**
* 线程池工具类
* <p>
* 根据CPU数量
*/
public class ThreadPoolManager {
private ThreadPoolExecutor executor;
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;//9
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;//17
private static final int KEEP_ALIVE = 1;
private static ThreadPoolManager mInstance;
public static ThreadPoolManager getInstance() {
if (mInstance == null) {
synchronized (ThreadPoolManager.class) {
if (mInstance == null) {
mInstance = new ThreadPoolManager();
}
}
}
return mInstance;
}
private ThreadPoolManager() {
executor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE,
KEEP_ALIVE,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
}
/**
* 执行任务
*/
public void execute(Runnable runnable) {
if (runnable == null) return;
executor.execute(runnable);
}
/**
* 移除任务
* <p>
* 注意:此方法起作用有一个必要的前提,就是这个任务还没有开始执行,如果已经开始执行了,就停止不了该任务了,这个方法就不会起作用
*/
public void remove(Runnable runnable) {
if (runnable == null) return;
executor.remove(runnable);
}
/**
* 提交任务
*/
public Future<?> submit(Runnable runnable) {
if (runnable == null) return null;
return executor.submit(runnable);
}
}
调用:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for (int i = 0; i < 9; i++) {
ThreadPoolManager.getInstance().execute(new Runnable() {
@Override
public void run() {
SystemClock.sleep(5000); //模拟延时执行的时间
}
});
}
}
}