1.Android中的线程
在操作系统中,线程是操作系统能够调度的最小单元,同时线程又是一种受限的系统资源,即线程不能无限制的产生,并且线程的创建和销毁都会有相应的开销。线程在Android开发的过程中是一个很重要的概念,从用途上区分大概可以分为两种主线程和子线程。主线程是指进程所拥有的线程,在Java中默认一个进程只有一个线程,而这个线程就是主线程,主线程又被称为UI线程主要处理界面相关的事情,因为用户会随时和界面进行交互,因此主线程在任何时候都必须保持着较高的响应速度,否则就会产生一种界面卡顿的感觉,甚至主线程阻塞5秒系统就会显示ANR对话框提示用户对应的应用处于无响应状态。而子线程又被称作工作线程,往往用于一些耗时的操作,除了主线程之外的都是子线程。
2.Android中的线程池
线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。Android中的线程池的概念来源于Java中的Executor,在Java中Executor是一个接口,线程池的真正实现为ThreadPoolExecutor,ThreadPoolExecutor提供了一系列的参数来配置线程池,通过传入不同的参数就可以得到不同作用的线程池。在代码中使用线程池进行优化,可以重用线程池中的线程,避免因为频繁的创建和销毁线程带来的性能上的损耗,同时线程池还能控制程序线程的最大并发数,避免因大量的线程之间相互争抢资源而导致阻塞的现象,线程池还能够对线程进行简单的管理,并提供定时执行以及指定时间间隔进行循环执行等功能。
3.线程池的使用
Android的线程池主要分为4类,分别为FixedThreadPool、CachedThreadPool、ScheduledThreadPool和SingleThreadExecutor,这4类线程池可以通过Executors所提供的工厂方法来得到,由于Android中的线程池都是直接或者间接的通过配置ThreadPoolExecutor来实现的,因此需要先了解ThreadPoolExecutor。
(1)ThreadPoolExecutor
ThreadPoolExecutor有四个重载方法,在这里主要介绍参数最多的那个构造方法,这样就可以知道其它方法参数的含义了。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
这里的构造方法有7个参数,接下来我们来看看这7个参数的含义:
对于线程中的任务队列来说,workQueue是一个BlockingQueue的类型,那么这个BlockingQueue又是什么呢?它是一个特殊的队列,当我们从BlockingQueue中取数据时,如果BlockingQueue是空的,则取数据的操作会进入到阻塞状态,当BlockingQueue中有了新数据时,这个取数据的操作又会被重新唤醒。同理,如果BlockingQueue中的数据已经满了,往BlockingQueue中存数据的操作又会进入阻塞状态,直到BlockingQueue中又有新的空间,存数据的操作又会被冲洗唤醒。
BlockingQueue有多种不同的实现类,主要使用的是以下几个:
ThreadPoolExecutor threadPoolExecutor =
new ThreadPoolExecutor(3, 5, 1, TimeUnit.SECONDS, new LinkedBlockingQueue(128));
for(int i = 0; i < 30; i++)
{
final int num = i;
Runnable runnable = new Runnable()
{
public void run()
{
try
{
Thread.sleep(2000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
Log.d("ThreadPoolExecutor", "Thread:" + num);
}
};
threadPoolExecutor.execute(runnable);
}
上面的代码中使用ThreadPoolExecutor创建了一个线程池管理线程,执行结果如下所示:
public abstract class AsyncTask {
private static final String LOG_TAG = "AsyncTask";
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final BlockingQueue sPoolWorkQueue =
new LinkedBlockingQueue(128);
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
从上面的代码中可以看出,核心线程数为手机CPU数量+1(cpu数量获取方式为Runtime.getRuntime().availableProcessors()),最大线程数为手机CPU数量×2+1,线程队列的大小为128,在以后使用线程池的过程中可以参考这个再结合自己的实际情况来配置参数。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
从它的构造方法的源代码中可以看出,核心线程数和最大线程数一样并且都是1,并且线程的超时时间为0,使用SingleThreadExecutor的一个最大好处就是可以避免我们去处理线程同步问题。
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
for(int i = 0; i < 30; i++)
{
final int num = i;
Runnable runnable = new Runnable()
{
public void run()
{
try
{
Thread.sleep(2000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
Log.d("SingleThreadPool", "Thread:" + num);
}
};
singleThreadPool.execute(runnable);
}
上面的代码中使用Executors.newSingleThreadExecutor()创建了一个线程池管理线程,执行结果如下所示:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for(int i = 0; i < 30; i++)
{
final int num = i;
Runnable runnable = new Runnable()
{
public void run()
{
try
{
Thread.sleep(2000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
Log.d("FixedThreadPool", "Thread:" + num);
}
};
fixedThreadPool.execute(runnable);
}
上面的代码中使用Executors.newFixedThreadPool()创建了一个线程池管理线程,执行结果如下所示:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
从它的构造方法的源代码中可以看出,CachedTreadPool中是没有核心线程的,但是它的最大线程数却为Integer.MAX_VALUE,而且它是有线程超时机制的,超时时间为60秒,这里它使用了SynchronousQueue作为线程队列,SynchronousQueue的特点上文已经说过了,这里不再赘述。由于CachedTreadPool的最大线程数为无限大,所以每当我们添加一个新任务进来的时候,如果线程池中有空闲的线程,则由该空闲的线程执行新任务,如果没有空闲线程,则创建新线程来执行任务,通过这样的机制就可以根据程序的运行情况自动来调整线程池中的线程数量。根据CachedThreadPool这样的特点,我们可以在有大量任务请求的时候使用CachedThreadPool,因为当CachedThreadPool中没有新任务的时候,它里边所有的线程都会因为超时而被终止。
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for(int i = 0; i < 30; i++)
{
final int num = i;
Runnable runnable = new Runnable()
{
public void run()
{
try
{
Thread.sleep(2000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
Log.d("CachedThreadPool", "Thread:" + num + "——" + Thread.currentThread().getName());
}
};
cachedThreadPool.execute(runnable);
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
从它的构造方法的源代码中可以看出,ScheduledThreadPool的核心线程数是固定的由我们传入的参数决定,最大线程数为Integer.MAX_VALUE,当非核心线程闲置时,则会被立即回收。
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
Runnable runnable = new Runnable()
{
public void run()
{
Log.d("ScheduledThreadPool", "Thread");
}
};
scheduledExecutorService.scheduleAtFixedRate(runnable, 1, 1, TimeUnit.SECONDS);
上面的代码中使用
Executors.newScheduledThreadPool()
创建了一个线程池管理线程,并且使用scheduleAtFixedRate()将线程加入到线程池中,执行结果如下所示:
以上Demo的源代码地址:点击打开链接