该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,如果能给各位看官带来一丝启发或者帮助,那真是极好的。
前言
上一篇说到了Android并发编程中的 原子类与并发容器,那么本篇呢,继续上一篇记录一下Android并发编程中常用的一些工具类,以及面试必问知识点--线程池.
并发工具类
CountDownLatch(等待多线程完成)
CountDownLatch允许一个或多个线程等待其他线程完成操作。
当我们需要用多个线程分解一些比较复杂任务时,这些任务通常符合下面两个规则:
- 任务可以分解为多个相互独立的子任务
- 所有子任务的综合结果即为该任务的结果
用法:
public class CountDownLatchTest {
static CountDownLatch c = new CountDownLatch(2);
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(1);
c.countDown();
System.out.println(2);
c.countDown();
}
}).start();
c.await();
System.out.println("3");
}
}
CountDownLatch的构造函数接收一个int类型的参数作为计数器,如果你想等待N个点完成,这里就传入N。当我们调用CountDownLatch的countDown方法时,N就会减1,CountDownLatch的await方法会阻塞当前线程,直到N变成零。
除了await()方法之外,也可以使用另外一个带指定时间的await方法——await(long time,TimeUnit unit),这个方法等待特定时间后,就会不再阻塞当前线程。
总结来说就是等待多个线程的完成,然后自己(调用await方法的线程)才运行.
CyclicBarrier(同步屏障)
同步屏障要做的事情是,让一个线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。
用法
public class CyclicBarrierTest {
static CyclicBarrier c = new CyclicBarrier(2);
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
try {
c.await();
} catch (Exception e) {
}
System.out.println(1);
}
}).start();
try {
c.await();
} catch (Exception e) {
}
System.out.println(2);
}
}
CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数
量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。当所有线程都到达了屏障时(即都调用了await方法)时,所有线程进行CPU竞争,由CPU调度运行.
Semaphore(控制线程数量)
Semaphore,信号量(令牌数).信号量这个概念跟我们到一个非常火的饭店排队领号吃饭一样,由于饭店的容量是有限的,只能容纳N个人,前面N个人被叫到号进去用餐,其他的人只能等待,直到饭店中有人离开,才会继续叫号.
Semaphore的构造方法Semaphore(int permits)接受一个整型的数字,表示可用的许可证数量。在上面的例子中这个数字就是饭店中能容纳的人数.正在饭店内用餐的人代表正在运行的线程,在外面的等待的人代表阻塞的线程,吃完饭离开的人代表已经运行完毕的线程.
用法
public class SemaphoreTest {
private static final int THREAD_COUNT = 30;
private static ExecutorServicethreadPool = Executors
.newFixedThreadPool(THREAD_COUNT);
private static Semaphore s = new Semaphore(10);
public static void main(String[] args) {
for (inti = 0; i< THREAD_COUNT; i++) {
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
s.acquire();
System.out.println("save data");
s.release();
} catch (InterruptedException e) {
}
}
});
}
threadPool.shutdown();
}
}
在代码中,虽然有30个线程在执行,但是只允许10个并发执行。Semaphore(10)表示允许10个线程获取许可证,也就是最大并发数是10。
Semaphore使用Semaphore的acquire()方法获取一个许可证,使用完之后调用release()方法归还许可证。还可以用tryAcquire()方法尝试获取许可证。
线程池
Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序
都可以使用线程池。在开发过程中,合理地使用线程池能够带来3个好处。
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用线程池,必须对其实现原理了如指掌。
Java中常用的线程池都是ThreadPoolExecutor不同的配置产生的以符合不同的场景.所以理解ThreadPoolExecutor至关重要.
下图是线程池的原理模型.
有了线程池的原理模型之后,我们再看在Java库中是如何实现这个模型的,下面我们来看ThreadPoolExecutor
ThreadPoolExecutor
Java的常用线程池
FixedThreadPoolExecutor
FixedThreadPool被称为可重用固定线程数的线程池。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
FixedThreadPoolExecutor是一种线程数量固定的线程池,当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭了.当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来.由于FixedThreadPool只有核心线程并且这些核心线程不会被回收,这意味着它能够更加快速的响应外界的请求.
SingleThreadExecutor详解
SingleThreadExecutor是使用单个worker线程的Executor.
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
SingleThreadPool内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行.
CachedThreadPool
CachedThreadPool是一个会根据需要创建新线程的线程池。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
CachedThreadPool是一种线程数量不定的线程池,它只有非核心线程,并且其最大线程数Integer.MAX_VALUE是一个很大的数,实际上就相当于最大线程数可以任意大.当线程池中的线程都处于活动状态时,线程池会创建新的线程来处理新任务,否则就会利用空闲的线程来处理新任务.线程池中的空闲线程都有超时机制,60秒,超过60秒的的闲置线程就会被回收.
CachedThreadPoll比较适合执行大量的耗时较少的任务
ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor继承自ThreadPoolExecutor。它的核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会被立即回收.它主要用来在给定的延迟之后运行任务,或者定期执行任务。
本篇总结
本篇呢,记录了一下并发编程中常用的一些工具类以及java或者Android面试中基本必问的只是点线程池.关于并发编程的记录暂告一段落.后面可能会出番外篇.最近也入职了新公司,忙着适应新公司的时候好像怠慢了自己的积累.后面尽量会按照一个月一篇的速度更新博客,感谢关注我的粉丝.
此致,敬礼