多线程编程(六)-Executor与ThreadPoolExecutor的使用

  • 使用Executors工厂类创建线程池

    1、使用newCachedThreadPool()方法创建无界线程池

      newCachedThreadPool()方法创建的是无界线程池,可以进行线程自动回收,此类线程池中存放线程个数理论值为Integer.MAX_VALUE最大值。

package com.wjg.unit4_2_2;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Run {
    public static void main(String[] args) throws InterruptedException {
        Run run = new Run();
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            executorService.execute(run.new MyRunnable(" "+(i+1)));
        }
        Thread.sleep(3000);
        System.out.println();
        System.out.println();
        for (int i = 0; i < 5; i++) {
            executorService.execute(run.new MyRunnable(" "+(i+1)));
        }
    }

    public class MyRunnable implements Runnable{
        private String username;

        public MyRunnable(String username) {
            this.username = username;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+" username="+username+" begin "+System.currentTimeMillis());
            System.out.println(Thread.currentThread().getName()+" username="+username+" end   "+System.currentTimeMillis());
        }

    }
}

执行结果:

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Monaco }
p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Monaco; min-height: 19.0px }

pool-1-thread-1 username= 1 begin 1488268086641

pool-1-thread-3 username= 3 begin 1488268086641

pool-1-thread-2 username= 2 begin 1488268086641

pool-1-thread-2 username= 2 end   1488268086641

pool-1-thread-4 username= 4 begin 1488268086642

pool-1-thread-4 username= 4 end   1488268086642

pool-1-thread-3 username= 3 end   1488268086641

pool-1-thread-1 username= 1 end   1488268086641

pool-1-thread-5 username= 5 begin 1488268086642

pool-1-thread-5 username= 5 end   1488268086642

pool-1-thread-5 username= 1 begin 1488268089647

pool-1-thread-3 username= 3 begin 1488268089648

pool-1-thread-4 username= 4 begin 1488268089648

pool-1-thread-1 username= 2 begin 1488268089647

pool-1-thread-1 username= 2 end   1488268089648

pool-1-thread-4 username= 4 end   1488268089648

pool-1-thread-3 username= 3 end   1488268089648

pool-1-thread-2 username= 5 begin 1488268089648

pool-1-thread-2 username= 5 end   1488268089648

pool-1-thread-5 username= 1 end   1488268089648

通过线程的名字,可以看出来线程是从池中取出来的,是可以复用的。

    2、使用newCachedThreadPool(ThreadFactory)定制线程工厂

      构造函数ThreadFactory是实现定制Thread的作用,具体可以看下面的例子。

package com.wjg.unit4_2_3;

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

public class Run {
    public static void main(String[] args) {
        Run run = new Run();
        MyThreadFactory factory = run.new MyThreadFactory();
        ExecutorService executorService = Executors.newCachedThreadPool(factory);
        executorService.execute(new Runnable() {

            @Override
            public void run() {
                System.out.println("当前线程的自定义名称为:"+ Thread.currentThread().getName());
            }
        });
    }

    public class MyThreadFactory implements ThreadFactory{

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            thread.setName("自定义名称:"+new Date());
            return thread;
        }

    }
}

执行结果:

当前线程的自定义名称为:自定义名称:Tue Feb 28 15:58:13 CST 2017

    3、使用newFixedThreadPool(int) 方法创建有界线程池

      此方法创建的是有界线程池,也就是池中的线程的个数可以指定最大值。

package com.wjg.unit4_2_4;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Run {
    public static void main(String[] args) {
        Run run = new Run();
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 5; i++) {
            executorService.execute(run.new MyRunnable(" "+(i+1)));
        }
    }

    public class MyRunnable implements Runnable{
        private String username;

        public MyRunnable(String username) {
            this.username = username;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+" username="+username+" begin "+System.currentTimeMillis());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" username="+username+" end   "+System.currentTimeMillis());
        }

    }
}

执行结果:

pool-1-thread-1 username= 1 begin 1488269132995

pool-1-thread-3 username= 3 begin 1488269132995

pool-1-thread-2 username= 2 begin 1488269132995

pool-1-thread-2 username= 2 end   1488269136000

pool-1-thread-3 username= 3 end   1488269136000

pool-1-thread-2 username= 4 begin 1488269136000

pool-1-thread-3 username= 5 begin 1488269136000

pool-1-thread-1 username= 1 end   1488269136000

pool-1-thread-2 username= 4 end   1488269139002

pool-1-thread-3 username= 5 end   1488269139002

通过执行结果可以看出,线程池中的线程最大数量为3。

    4、使用newSingleThreadExecutor()方法创建单一线程池

      此方法可以创建单一线程池,线程池里只有一个线程,单一线程池可以实现以队列的方式来执行任务。

package com.wjg.unit4_2_5;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Run {
    public static void main(String[] args) {
        Run run = new Run();
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 5; i++) {
            executorService.execute(run.new MyRunnable(" "+(i+1)));
        }
    }

    public class MyRunnable implements Runnable{
        private String username;

        public MyRunnable(String username) {
            this.username = username;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+" username="+username+" begin "+System.currentTimeMillis());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" username="+username+" end   "+System.currentTimeMillis());
        }

    }
}

执行结果:

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Monaco }

pool-1-thread-1 username= 1 begin 1488269392403

pool-1-thread-1 username= 1 end   1488269395409

pool-1-thread-1 username= 2 begin 1488269395409

pool-1-thread-1 username= 2 end   1488269398412

pool-1-thread-1 username= 3 begin 1488269398413

pool-1-thread-1 username= 3 end   1488269401418

pool-1-thread-1 username= 4 begin 1488269401418

pool-1-thread-1 username= 4 end   1488269404422

pool-1-thread-1 username= 5 begin 1488269404422

pool-1-thread-1 username= 5 end   1488269407423

由执行结果的线程名字可以看出,线程池中只有一个线程。

  • ThreadPoolExecutor的使用

    类ThreadPoolExecutor可以非常方便的创建线程池对象。

    常用的构造方法有ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue)

    参数解释如下:

    corePoolSize:池中所保存的线程数,包括空闲线程数,也就是核心池的大小。

    maximumPoolSize:池中允许的最大线程数

    keepAliveTime:当线程数量大于corePoolSize值时,在没有超过指定的时间内是不从线程池中将空闲线程删除的,如果超过此时间,则删除。

    unit:keepAliveTime参数的时间单位。

    workQueue:执行前用于保持任务的队列。此队列仅保存由execute方法提交的Runnable任务。

    为了更好地理解这些参数在使用上的一些关系,可以将它们进行详细化的注释:

    (1)A代表execute(runnable)欲执行的runnable的数量;

    (2)B代表corePoolSize;

    (3)C代表maximumPoolSize;

    (4)D代表A-B(假设A>=B);

    (5)E代表newLinkedBlockingDeque<Runnable>()队列,无构造函数。

    (6)F代表SynchronousQueue队列;

    (7)G代表keepAliveTime;

    在使用线程池的过程下,会出现以下的集中情况:

    (1)如果A<=B,那么马上创建线程运行这个任务,并不放入扩展队列Queue中,其他参数功能忽略;

    (2)如果A>B&&A<=C&&E,则C和G参数忽略,并把D放入E中等待被执行;

    (3)如果A>B&&A<=C&&F,则C和G参数有效,并且马上创建线程运行这些任务,而不把D放入F中,D执行完任务后在指定时间后发生超时时将D进行清除。

    (4)如果A>B&&A>C&&E,则C和G参数忽略,并把D放入E中等待被执行;

    (5)如果A>B&&A>C&&F,则处理C的任务,其他任务则不再处理抛出异常;

    方法getActiveCount()的作用是取得有多少个线程正在执行任务。

    方法getPoolSize()的作用是获得当前线程池里面有多少个线程,这些线程数包括正在执行任务的线程,也包括正在休眠的线程。

    方法getCompletedTaskCount()的作用是取得有多少个线程已经执行完任务了。

    方法getCorePoolSize()的作用是取得构造方法传入的corePoolSize参数值。

    方法getMaximumPoolSize()的作用是取得构造方法传入的maximumPoolSize的值。

    方法getTaskCount()的作用是取得有多少个任务发送给了线程池。

    方法shutdown()的作用是使当前未执行完的线程继续执行,而不再添加新的任务task,该方法不会阻塞,调用之后,主线程main马上结束,而线程池会继续运行直到所有任务执行完才会停止。

    方法shutdownNow()的作用是中断所有的任务task,并且抛出InterruptedException异常,前提是在Runnable中使用if(Thread.currentThread().isInterrupted()==true)语句来判断当前线程的中断状态,而未执行的线程不再执行,也就是从执行队列中清除。如果不手工加if语句及抛出异常,则池中正在运行的线程知道执行完毕,而未执行的线程不再执行,也从执行队列中清除。

    方法isShutDown()的作用是判断线程池是否已经关闭。  

    方法isTerminating()的作用是判断线程池是否正在关闭中。

    方法isTerminated()的作用是判断线程池是否已经关闭。

    方法awaitTermination(long timeout,TimeUnit unit)的作用是查看在指定的时间之内,线程池是否已经终止工作,也就是最多等待多少时间后去判断线程池是否已经终止工作。

    方法allowsCoreThreadTimeOut(boolean) 的作用是配置核心线程是否有超时的效果。

    方法prestartCoreThread()的作用是每调用一次就创建一个核心线程,返回值为boolean。

    方法prestartAllCoreThreads()的作用是启动全部核心线程,返回值是启动核心线程的数量。

  • 线程池ThreadPoolExecutor的拒绝策略

    线程池中的资源全部被占用的时候,对新添加的Task任务有不同的处理策略,在默认的情况ThreadPoolExecutor类中有4个不同的处理方式:

    (1)AbortPolicy:当任务添加到线程池中被拒绝时,它将抛出RejectedExecutionException异常。

    (2)CallerRunsPolicy:当任务添加到线程池被拒绝时,会使用调用线程池的Thread线程对象处理被拒绝的任务。

    (3)DiscardOldestPolicy:当任务添加到线程池被拒绝时,线程池会放弃等待队列中最旧的未处理任务,然后将拒绝的任务添加到等待队列中。

    (4)DiscardPolicy:当任务添加到线程池中被拒绝时,线程池将丢弃被拒绝的任务。

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Monaco }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Monaco }

时间: 2024-10-21 13:01:32

多线程编程(六)-Executor与ThreadPoolExecutor的使用的相关文章

多线程编程(六)--Callable&amp;Future

Thread类和Runnable接口和Java内存管理模型使得多线程编程简单直接.但是Thread类和Runnable接口都不允许声明检查型异常,也不能定义返回值. Callable接口和Future接口的引入以及它们对线程池的支持优雅的解决了这两个问题. Callable接口类似于Runnable接口,Callable接口被线程执行后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值.Future取得的结果类型和Callable返回的结果类型必须

Android多线程编程之线程池学习篇(一)

Android多线程编程之线程池学习篇(一) 一.前言 Android应用开发中多线程编程应用比较广泛,而应用比较多的是ThreadPoolExecutor,AsyncTask,IntentService,HandlerThread,AsyncTaskLoader等,为了更详细的分析每一种实现方式,将单独成篇分析.后续篇章中可能涉及到线程池的知识,特此本篇分析为何使用线程池,如何使用线程池以及线程池的使用原理. 二.Thread Pool基础 进程代表一个运行中的程序,一个运行中的Android

汪大神Java多线程编程实战

课程目录:├─1│  ├─Java并发编程.png│  ├─源码+ppt.rar│  ├─高并发编程第一阶段01讲.课程大纲及主要内容介绍.wmv│  ├─高并发编程第一阶段02讲.简单介绍什么是线程.wmv│  ├─高并发编程第一阶段03讲.创建并启动线程.mp4│  ├─高并发编程第一阶段04讲.线程生命周期以及start方法源码剖析.mp4│  ├─高并发编程第一阶段05讲.采用多线程方式模拟银行排队叫号.mp4│  ├─高并发编程第一阶段06讲.用Runnable接口将线程的逻辑执行单元

多线程编程(进程和线程)

多线程编程(进程和线程) 1.进程:指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程可以启动多个线程. 2.线程:指程序中一个执行流程,一个进程中可以运行多个线程. 一.创建线程(两种方式) 二.线程的5种状态( New,Runnable,Running,Block,Dead ): 三.线程的优先级 四.守护线程 /精灵线程/后台线程 五.方法 六.同步代码锁(synchronized) 一.创建线程(两种方式): 方式1:采用继承Thread的方法 第一,继承 Thre

多线程编程1-NSThread

前言 每个iOS应用程序都有个专门用来更新显示UI界面.处理用户触摸事件的主线程,因此不能将其他太耗时的操作放在主线程中执行,不然会造成主线程堵塞(出现卡机现象),带来极坏的用户体验.一般的解决方案就是将那些耗时的操作放到另外一个线程中去执行,多线程编程是防止主线程堵塞,增加运行效率的最佳方法. iOS中有3种常见的多线程编程方法: 1.NSThread 这种方法需要管理线程的生命周期.同步.加锁问题,会导致一定的性能开销 2.NSOperation和NSOperationQueue 是基于OC

java多线程编程

一.多线程的优缺点 多线程的优点: 1)资源利用率更好2)程序设计在某些情况下更简单3)程序响应更快 多线程的代价: 1)设计更复杂虽然有一些多线程应用程序比单线程的应用程序要简单,但其他的一般都更复杂.在多线程访问共享数据的时候,这部分代码需要特别的注意.线程之间的交互往往非常复杂.不正确的线程同步产生的错误非常难以被发现,并且重现以修复. 2)上下文切换的开销当CPU从执行一个线程切换到执行另外一个线程的时候,它需要先存储当前线程的本地的数据,程序指针等,然后载入另一个线程的本地数据,程序指

Java多线程编程总结

Java线程:概念与原理 Java线程:创建与启动 Java线程:线程栈模型与线程的变量 Java线程:线程状态的转换  Java线程:线程的同步与锁 Java线程:线程的交互 Java线程:线程的调度-休眠  Java线程:线程的调度-优先级 Java线程:线程的调度-让步 Java线程:线程的调度-合并 Java线程:线程的调度-守护线程 Java线程:线程的同步-同步方法 Java线程:线程的同步-同步块  Java线程:并发协作-生产者消费者模型 Java线程:并发协作-死锁 Java线

C#多线程编程

C#多线程编程 一.使用线程的理由 1.可以使用线程将代码同其他代码隔离,提高应用程序的可靠性. 2.可以使用线程来简化编码. 3.可以使用线程来实现并发执行. 二.基本知识 1.进程与线程:进程作为操作系统执行程序的基本单位,拥有应用程序的资源,进程包含线程,进程的资源被线程共享,线程不拥有资源. 2.前台线程和后台线程:通过Thread类新建线程默认为前台线程.当所有前台线程关闭时,所有的后台线程也会被直接终止,不会抛出异常. 3.挂起(Suspend)和唤醒(Resume):由于线程的执行

java笔记--使用线程池优化多线程编程

使用线程池优化多线程编程 认识线程池 在Java中,所有的对象都是需要通过new操作符来创建的,如果创建大量短生命周期的对象,将会使得整个程序的性能非常的低下.这种时候就需要用到了池的技术,比如数据库连接池,线程池等. 在java1.5之后,java自带了线程池,在util包下新增了concurrent包,这个包主要作用就是介绍java线程和线程池如何使用的. 在包java.util.concurrent下的 Executors类中定义了Executor.ExecutorService.Sche