该文章是一个系列文章,是本人在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至关重要.

下图是线程池的原理模型.

0b76f6015637

在这里插入图片描述

有了线程池的原理模型之后,我们再看在Java库中是如何实现这个模型的,下面我们来看ThreadPoolExecutor

ThreadPoolExecutor

0b76f6015637

在这里插入图片描述

Java的常用线程池

FixedThreadPoolExecutor

FixedThreadPool被称为可重用固定线程数的线程池。

public static ExecutorService newFixedThreadPool(int nThreads) {

return new ThreadPoolExecutor(nThreads, nThreads,

0L, TimeUnit.MILLISECONDS,

new LinkedBlockingQueue());

}

FixedThreadPoolExecutor是一种线程数量固定的线程池,当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭了.当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来.由于FixedThreadPool只有核心线程并且这些核心线程不会被回收,这意味着它能够更加快速的响应外界的请求.

SingleThreadExecutor详解

SingleThreadExecutor是使用单个worker线程的Executor

public static ExecutorService newSingleThreadExecutor() {

return new FinalizableDelegatedExecutorService

(new ThreadPoolExecutor(1, 1,

0L, TimeUnit.MILLISECONDS,

new LinkedBlockingQueue()));

}

SingleThreadPool内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行.

CachedThreadPool

CachedThreadPool是一个会根据需要创建新线程的线程池。

public static ExecutorService newCachedThreadPool() {

return new ThreadPoolExecutor(0, Integer.MAX_VALUE,

60L, TimeUnit.SECONDS,

new SynchronousQueue());

}

CachedThreadPool是一种线程数量不定的线程池,它只有非核心线程,并且其最大线程数Integer.MAX_VALUE是一个很大的数,实际上就相当于最大线程数可以任意大.当线程池中的线程都处于活动状态时,线程池会创建新的线程来处理新任务,否则就会利用空闲的线程来处理新任务.线程池中的空闲线程都有超时机制,60秒,超过60秒的的闲置线程就会被回收.

CachedThreadPoll比较适合执行大量的耗时较少的任务

ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor继承自ThreadPoolExecutor。它的核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会被立即回收.它主要用来在给定的延迟之后运行任务,或者定期执行任务。

点击+关注哦

0b76f6015637

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐