最近在项目中接触到了很多有关于多线程方面的东西,并且刚好前段时间看了Java并发编程实战那本说, 打算首先介绍一下,Java多线程相关的基础,例如Thread,Runnable。虽然这个极其的基础,但是我觉得任何东西都 应该会分几篇博客来总结相关的知识。当然其中肯定会有一些错误之处,欢迎留言指出。我会及时更正。 以上相关所有知识点,有许多借鉴自其他的大神的博客,由于看了许多,没办法一一列举,在此表示感谢。许多概念相关的知识来自于《Java并发编程实战》那本书。 一、Thread 继承Thread类,重写run方法。 我觉得Thread应该是被最经常用到的。当只需要一个简单的线程,不需要管理线程时直接: new Thread(){ @Override public void run() { //请求网络等 } }.start();123456123456 一个匿名内部类就写完了,相信很多人都写过这样的代码。需要注意的是,如果在Activity中写这样的代码很容易造成内存泄露。 二、 Runnable 实现Runnable接口重写run()方法 public class DisableRunnable implements Runnable{ @Override public void run() { //请求网络等 } }12345671234567 Runnable 是执行任务的单元,需要用Thread包装一下才可以执行。 每当我们看到这样的代码时: new Thread(){runnable}.start(); 当你考虑用更灵活的策略来执行任务时,可以考虑利用线程池代替Thread。 三、 线程池: 下面主要说一下线程池相关的总结, 最顶层接口 Executor public interface Executor { void execute(Runnable command); }1234512345 ExecutorService接口 继承Executor 定义了一些有关于生命周期的方法。 public interface ExecutorService extends Executor { void shutdown(); List<Runnable> shutdownNow(); boolean isShutdown(); boolean isTerminated(); boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException; <T> Future<T> submit(Callable<T> task); <T> Future<T> submit(Runnable task, T result); Future<?> submit(Runnable task); <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException; <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException; <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }123456789101112131415161718192021222324252627282930123456789101112131415161718192021222324252627282930 ThreadPoolExecutor 间接实现了ExecutorService 是真正做事的线程池。最常见的几种线程池都是它的实现。 ThreadPoolExecutor 常用的构造方法: public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler){}123456123456 相关参数解释: 1.当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。 注意: 四、这里BlockingQueue SynchronousQueue LinkedBlockingQueue ArrayBlockingQueue PriorityBlockingQueue 优先级队列 Executors 线程池工具类,用于生产线程池。 五、几类比较常见的线程池: public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }1234512345 解释:构建一个固定数目的线程池。corePoolSize 与 maximumPoolSize 数值相等。当线程池中没有空闲线程时,利用 LinkedBlockingQueue public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }123456123456 解释:构建一个单线程(只有一个线程,单不一定是同一个线程)的线程池。corePoolSize 与 maximumPoolSize 均为1 .利用无界的LinkedBlockingQueue 作为保存任务的队列,这样就保证无论提交多少个人物,线程池只能有一个线程在运行,其余的任务均保存在 LinkedBlockingQueue 队列中。 public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }1234512345 解释:构建一个具有缓存功能的线程池,corePoolSize = 0, maximumPoolSize = Integer.MAX_VALUE 。队列用的是SynchronousQueue。每加入一个任务,线程池便会构建一个线程将它取出执行(如果有空闲的线程,会利用空闲的线程,而不会创建新的线程),这样,加入多少个任务便会创建相同个数的线程。所以这个线程池的 maximumPoolSize 为Integer.MAX_VALUE。这就保证了线程池的最大线程数是无界的,理论上线程池可以有任意个线程。当线程执行完任务后,超过60秒的空闲时间即被回收销毁。 六、饱和策略: RejectedExecutionHandler 参数用来表示饱和策略。即表示当有界队列已满,并且当前线程池中的线程数已达到 maximumPoolSize ,这时再提交任务,会交给RejectedExecutionHandler 来处理。 注意:这里一定是有界队列,因为无界队列我们认为是永远也无法填满的(SynchronousQueue直接由生产者提交给工作线程),那么也就永不到饱和策略了。 public interface RejectedExecutionHandler { void rejectedExecution(Runnable r, ThreadPoolExecutor executor); }1234512345 RejectedExecutionHandler 本身是一个接口。在Executors中提供了几个实现类。 1)AbortPolicy:中止,executor抛出未检查RejectedExecutionException,调用者捕获这个异常,然后自己编写能满足自己需求的处理代码。 2)DiscardRunsPolicy:遗弃最旧的,选择丢弃的任务,是本应接下来就执行的任务。 3)DiscardPolicy:遗弃会默认放弃最新提交的任务(这个任务不能进入队列等待执行时) 4)CallerRunsPolicy:调用者运行,既不会丢弃哪个任务,也不会抛出任何异常,把一些任务推回到调用者那里,以此减缓新任务流。它不会在池线程中执行最新提交的任务,但它会在一个调用了execute的线程中执行。 七、线程工厂: public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {} public interface ThreadFactory { Thread newThread(Runnable r); }1234567891011121312345678910111213 ThreadFactory 在 Executors 中也提供了几个实现类,一般博主所接触过的都是 DefaultThreadFactory 。当然我们可以自己定制, 八、常见的线程池的使用: 自定义线程池: private final static ExecutorService executorService = new ThreadPoolExecutor( 5, 10, 30L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { //DO nothing } });123456789101112123456789101112 这里我们用的是自定义的线程池。我们定义了一个核心线程池为5的线程池,线程池内最大的线程池数为10。 当然由于我们这里定义的缓存队列为LinkedBlockingQueue, 没有制定队列大小,那么其默认无Integer.MAX_VALUE。 饱和策略也是自定的,这里当达到执行饱和策略时,什么都不做,直接丢弃。 public void request() { executorService.execute(new Runnable() { @Override public void run() { try { //请求网络等。 } catch (Exception e) { e.printStackTrace(); } } }); } 利用Executors 提供的线程池: public void request() { ExecutorService executor = Executors.newFixedThreadPool(5); executor.execute(new Runnable() { @Override public void run() { try { //请求网络等。 } catch (Exception e) { e.printStackTrace(); } } }); }1234567891011121314151617181920212223242526272829303112345678910111213141516171819202122232425262728293031 OK, 线程池相关就介绍到这里,下一篇会介绍Future,FutureTask, Callable 等等相关知识。 |
|