Java多线程_线程池ThreadPoolExecutor

文章目录

一、使用Executors工厂类创建线程池ThreadPoolExecutor

JVM频繁地处理线程对象的创建和销毁,如果请求的执行时间很短,则有可能花在创建和销毁线程对象上的时间大于真正执行任务的时间,导致系统性能会大幅降低。
线程池主要用于支持高并发的访问处理,线程池类ThreadPoolExecutor实现了Executor接口。线程池核心原理是创建一个“线程池”(ThreadPool),在池中对线程对象进行管理,包括创建和销毁,使用池的时候只需要执行具体的任务即可,线程对象的处理都在池中被封装了。

1、使用Executors工厂类的newCachedThreadPool()创建无界线程池

Executors工厂类对ThreadPoolExecutor线程池进行封装,直接调用即可。

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

使用Executors类的newCachedThreadPool()方法创建无界线程池,可以进行线程的自动回收。
“无界线程池”就是池中存放线程个数是理论上的最大值,即可以存放Integer.MAX_VALUE。

  1. 创建threadpoolexecutor.myrunnable包,再创建线程类MyRunnable

    package threadpoolexecutor.myrunnable;
    
    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());
        }
    }
    
    
  2. 创建threadpoolexecutor.run包,再创建类Run2

    package threadpoolexecutor.run;
    
    import threadpoolexecutor.myrunnable.MyRunnable;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Run2 {
        public static void main(String[] args) throws InterruptedException {
            ExecutorService executorService = Executors.newCachedThreadPool();
            for (int i = 0; i < 5; i++) {
                executorService.execute(new MyRunnable(""+(1+i)));
            }
            Thread.sleep(1000);
            System.out.println("");
            for (int i = 0; i < 5; i++) {
                executorService.execute(new MyRunnable(""+(1+i)));
            }
    
        }
    }
    
    

    MyRunnable通过构造方法给内部的username属性赋值,在run方法中打印开始和结束的当前线程池中的线程的名字。
    启动类Run2中,用循环调用线程池的execute方法后,在该线程池中创建5个线程,休眠一秒是为了让线程池的线程把任务执行完,处于闲置状态并且不会被销毁,再次使用循环调用线程池的execute方法,此时并未创建新线程,而是从线程池中取出5个闲置的线程进行复用。

1.2 使用newCacheThreadPool(ThreadFactory)方法定制线程工厂

无界线程池中创建线程类的过程是可以通过ThreadFactory来定制的。

  1. 创建threadpoolexecutor.mythreadfactory包,再创建类MyThreadFactory

    package threadpoolexecutor.mythreadfactory;
    
    import java.util.concurrent.ThreadFactory;
    
    public class MyThreadFactory implements ThreadFactory {
    
        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            thread.setName("定制池的线程对象名"+Math.random());
            return thread;
        }
    }
    
    
  2. 创建threadpoolexecutor.run包,再创建类Run3

    package threadpoolexecutor.run;
    
    import threadpoolexecutor.mythreadfactory.MyThreadFactory;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Run3 {
        public static void main(String[] args) {
            MyThreadFactory myThreadFactory = new MyThreadFactory();
            ExecutorService executorService = Executors.newCachedThreadPool(myThreadFactory);
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("我在运行"+System.currentTimeMillis()+" "+Thread.currentThread().getName());
                }
            });
        }
    }
    
    

    MyThreadFactory通过实现ThreadFactory接口的newThread方法,来定制线程工厂。
    启动类Run3在实例化MyThreadFactory类后,将其传入到无界线程池中,从而实现定制线程工厂。此时线程池调用executor方法后,使用的是线程工厂的线程对象。
    即ThreadPoolExecutor使用ThreadFactory创建Thread对象。若不传入定制的线程工厂,则采用内部默认的线程工厂DefaultThreadFactory。

2、使用Executors工厂类的newFixedThreadPool(int)创建有界线程池

  1. 创建threadpoolexecutor.myrunnable包,再创建线程类MyRunnable2

    package threadpoolexecutor.myrunnable;
    
    public class MyRunnable2 implements Runnable{
        private String username;
        public MyRunnable2(String username){
            this.username = username;
        }
        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName()+" username="+username+" begin"+System.currentTimeMillis());
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName()+" username="+username+" end"+System.currentTimeMillis());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    
  2. 创建threadpoolexecutor.run包,再创建类Run4

    package threadpoolexecutor.run;
    
    import threadpoolexecutor.myrunnable.MyRunnable2;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Run4 {
        public static void main(String[] args) throws InterruptedException {
            ExecutorService executorService = Executors.newFixedThreadPool(4);
            for (int i = 0; i < 5; i++) {
                executorService.execute(new MyRunnable2(""+(1+i)));
            }
            for (int i = 0; i < 5; i++) {
                executorService.execute(new MyRunnable2(""+(1+i)));
            }
    
        }
    }
    
    

    MyRunnable2通过构造方法给内部的username属性赋值,在run方法中打印开始和结束的当前线程池中的线程的名字,并且在开始和结束中间休眠2s。
    启动类Run4中,用循环调用5次线程池的execute方法后,因为调用的是Executors的newFixedThreadPool方法,创建的是有界线程池,设置界限为4个,所以在该线程池中只能创建4个线程,线程池的4个线程把任务执行完,处于闲置状态并且不会被销毁,后面的调用executor方法并未创建新线程,而是从线程池中取出4个闲置的线程进行复用。

3、使用Executors工厂类的newSingleThreadExecutor()创建单一线程池

单一线程池可以实现以队列的方式来执行任务。即采用单例模式的设计。

  1. 创建threadpoolexecutor.myrunnable包,再创建线程类MyRunnable3

    package threadpoolexecutor.myrunnable;
    
    public class MyRunnable3 implements Runnable{
        private String username;
        public MyRunnable3(String username){
            this.username = username;
        }
        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName()+" username="+username+" begin"+System.currentTimeMillis());
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName()+" username="+username+" end"+System.currentTimeMillis());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    
  2. 创建threadpoolexecutor.run包,再创建类Run5

    package threadpoolexecutor.run;
    
    import threadpoolexecutor.myrunnable.MyRunnable3;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Run5 {
        public static void main(String[] args) throws InterruptedException {
            ExecutorService executorService = Executors.newSingleThreadExecutor();
            for (int i = 0; i < 3; i++) {
                executorService.execute(new MyRunnable3(""+(1+i)));
            }
    
        }
    }
    
    

    MyRunnable3通过构造方法给内部的username属性赋值,在run方法中打印开始和结束的当前线程池中的线程的名字,并且在开始和结束中间休眠2s。
    启动类Run5中,用循环调用3次线程池的execute方法后,因为调用的是Executors的newSingleThreadExecutor方法,创建的是单一线程池,即在该线程池中只能创建1个线程对象,线程池的1个线程把任务执行完,处于闲置状态并且不会被销毁,后面的调用executor方法并未创建新线程,而是从线程池中取出这个闲置的线程进行复用。

二、ThreadPoolExecutor的使用1

ThreadPoolExecutor可以非常方便地创建线程池对象,而不需要去设计大量的new实例化Thread相关的代码。

1、ThreadPoolExecutor最常用的构造方法

  1. public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,
    long keepAliveTime,TimeUnit unit,BlockingQueue workQueue)
    参数:
    1)corePoolSize:池中至少要保留的线程数,该属性就是定义corePool核心池的大小。
    2)maximumPoolSize:池中允许的最大线程数,maximumPoolSize包含corePoolSize。
    3)keepAliveTime:当线程数量大于corePoolSize值时,在没有超过指定的时间内是不能将线程池中的空闲线程删除的;如果超过指定时间,则删除空闲线程。即删除线程池中超过corePoolSize的空闲线程。
    4)unit:keepAliveTime的时间单位
    5)workQueue:执行前用于保存任务的队列。此队列仅保存由executor方法提交的Runnable任务。

2、构造方法前两个参数与getCorePoolSize()和getMaximumPoolSize()方法

  1. 创建threadpoolexecutor.run包,再创建类Run6
    package threadpoolexecutor.run;
    
    import java.util.concurrent.LinkedBlockingDeque;
    import java.util.concurrent.SynchronousQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class Run6 {
        public static void main(String[] args) {
            ThreadPoolExecutor executor = new ThreadPoolExecutor(6,8,5, TimeUnit.SECONDS,new LinkedBlockingDeque<Runnable>());
            System.out.println(executor.getCorePoolSize());
            System.out.println(executor.getMaximumPoolSize()+"\t");
            executor = new ThreadPoolExecutor(6,8,5,TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
            System.out.println(executor.getCorePoolSize());
            System.out.println(executor.getMaximumPoolSize());
        }
    }
    
    
    即getCorePoolSize()和getMaximumPoolSize()方法,获取的是ThreadPoolExexcutor通过构造方法初始化时传入的参数,跟队列本身能否支持保存多个任务(线程)没关系。

3、第五个参数使用LinkedBlockingQueue队列

  1. 创建threadpoolexecutor.run包,再创建类Run7

    package threadpoolexecutor.run;
    
    import java.util.concurrent.*;
    
    public class Run7 {
        public static void main(String[] args) throws InterruptedException {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(Thread.currentThread().getName()+" run"+System.currentTimeMillis());
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            };
    
            ThreadPoolExecutor executor = new ThreadPoolExecutor(6,8,5, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
            //添加7个任务,此时会将多于corePoolSize的任务加入到队列LinkedBlockingQueue中
            executor.execute(runnable);
            executor.execute(runnable);
            executor.execute(runnable);
            executor.execute(runnable);
            executor.execute(runnable);
            executor.execute(runnable);
            executor.execute(runnable);
            Thread.sleep(300);
            System.out.println("A executor.getCorePoolSize()="+executor.getCorePoolSize());
            System.out.println("A executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
            System.out.println("A executor.getPoolSize()="+executor.getPoolSize());
            System.out.println("A executor.getQueue().size()="+executor.getQueue().size());
            Thread.sleep(10000);
            System.out.println("10s后打印结果");
            System.out.println("B executor.getCorePoolSize()="+executor.getCorePoolSize());
            System.out.println("B executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
            System.out.println("B executor.getPoolSize()="+executor.getPoolSize());
            System.out.println("B executor.getQueue().size()="+executor.getQueue().size());
        }
    }
    
    

    1)第五个参数传入队列LinkedBlockingQueue的好处在于没有大小限制,即优点是队列容量非常大,此时发现线程中运行的线程数量不会超过corePoolSize的值,因为其他多余的任务(线程)会被存放到LinkedBlockingQueue队列中,此时的KeepAliveTime参数无意义。
    在线程池中设置的corePoolSize为6,在线程池中添加了7个任务,所以此时有一个会被存放在队列LinkedBlockingQueue中,然后等待线程池中的线程空闲后,进行线程复用。A和B打印的信息唯一不同的是队列中的数量。
    2)若此时使用SynchronousQueue队列,则A和B打印的信息唯一不同的是线程池中的线程数量。因为该队列不能存放任务,所以会在线程池中创建新线程运行任务。

4、超过设置的线程池最大值+队列容量的数值时

  1. 创建threadpoolexecutor.myrunnable包,再创建线程类MyRunnable4

    package threadpoolexecutor.myrunnable;
    
    public class MyRunnable4 implements Runnable{
        private String username;
        public MyRunnable4(String username){
            this.username = username;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName()+" username="+username+" begin"+System.currentTimeMillis());
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    
  2. 创建threadpoolexecutor.run包,再创建类Run8

    package threadpoolexecutor.run;
    
    import threadpoolexecutor.myrunnable.MyRunnable4;
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.RejectedExecutionHandler;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class Run8 {
        public static void main(String[] args) throws InterruptedException {
            MyRunnable4 m1 = new MyRunnable4("M1");
            MyRunnable4 m2 = new MyRunnable4("M2");
            MyRunnable4 m3 = new MyRunnable4("M3");
            MyRunnable4 m4 = new MyRunnable4("M4");
            MyRunnable4 m5 = new MyRunnable4("M5");
            MyRunnable4 m6 = new MyRunnable4("M6");
            MyRunnable4 m7 = new MyRunnable4("M7");
    
            ThreadPoolExecutor executor = new ThreadPoolExecutor(2,4,5, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(2));
            executor.setRejectedExecutionHandler(new RejectedExecutionHandler() {
                @Override
                public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                    System.out.println(((MyRunnable4)r).getUsername()+"不执行");
                }
            });
            //添加2个任务,此时使用核心线程池的线程
            executor.execute(m1);
            Thread.sleep(1000);
            executor.execute(m2);
            Thread.sleep(1000);
            //此时会将多于corePoolSize的2个任务加入到队列LinkedBlockingQueue中
            executor.execute(m3);
            Thread.sleep(1000);
            executor.execute(m4);
            Thread.sleep(1000);
            //此时会新建2个线程
            executor.execute(m5);
            Thread.sleep(1000);
            executor.execute(m6);
            Thread.sleep(1000);
            //此时超过最大值maximumPoolSize,超过1个,不执行
            executor.execute(m7);
    		System.out.println("A executor.getCorePoolSize()="+executor.getCorePoolSize());
        	System.out.println("A executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
       		System.out.println("A executor.getPoolSize()="+executor.getPoolSize());
        	System.out.println("A executor.getQueue().size()="+executor.getQueue().size());
        	Thread.sleep(14000);
        	System.out.println("14s后打印结果");
        	System.out.println("B executor.getCorePoolSize()="+executor.getCorePoolSize());
        	System.out.println("B executor.getMaximumPoolSize()="+executor.getMaximumPoolSize());
        	System.out.println("B executor.getPoolSize()="+executor.getPoolSize());
        	System.out.println("B executor.getQueue().size()="+executor.getQueue().size());
    
        }
    }
    

    创建一个ThreadPoolExecutor对象,此时核心线程池数为2,最大值为4,队列容量为2。先将任务给到核心线程,如果不够,再将多的部分给到队列,最后再给到线程数最大值与核心线程数之间的线程,此时还有多余的任务装不下,就会打印不执行。
    即在添加任务超过最大值+队列容量时,会打印不执行。
    即执行顺序是任务12在核心线程数中运行,任务34在队列容量中等待,任务56在maximumPoolSize-corePoolSize线程数中运行,任务7不执行,此时复用线程执行任务34。
    后面因为任务12执行完后,这2个线程继续执行任务34,任务6执行的时候,后面休眠1秒,所以任务的时候剩余9秒+5秒的空闲线程过期时间keepAliveTime为14秒,此时经过14秒后,除了核心线程池数外的线程被清除。此时线程池的线程为2,即核心线程池数。

三、ThreadPoolExecutor的使用2

1、方法shutdown()和shutdownNow()

1)public void shutdown()的作用是使当前未执行完的任务继续执行,而队列中未执行的任务继续执行,不删除队列中的任务,不再允许添加新任务,同时shutdown()方法不会阻塞。
2)public List shutdownNow()的作用是使当前未执行完的任务继续执行,而队列中未执行的任务不再执行,删除队列中的任务并返回,不再允许添加新任务,同时shutdownNow()方法不会阻塞。
即这两个方法的区别在于队列的任务会不会继续执行以及删除。
这两个方法是用来销毁线程池的。

1.1 shutdown()方法的使用

  1. 创建threadpoolexecutor.myrunnable包,再创建线程类MyRunnable5

    package threadpoolexecutor.myrunnable;
    
    public class MyRunnable5 implements Runnable{
        @Override
        public void run() {
            try {
                System.out.println(" begin"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
                Thread.sleep(3000);
                System.out.println(" end"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    
  2. 创建threadpoolexecutor.run包,再创建类Run9

    package threadpoolexecutor.run;
    
    
    import threadpoolexecutor.myrunnable.MyRunnable5;
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class Run9 {
        public static void main(String[] args) throws InterruptedException {
            MyRunnable5 myRunnable5 = new MyRunnable5();
            ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
            executor.execute(myRunnable5);
            executor.execute(myRunnable5);
            executor.execute(myRunnable5);
            executor.execute(myRunnable5);
            Thread.sleep(1000);
            executor.shutdown();
            executor.execute(myRunnable5);
            System.out.println("main end!");
        }
    }
    
    

    MyRunnable5打印开始和结束标志,中间休眠3秒。
    Run9启动类创建线程池后,调用线程池的execute方法将MyRunnable5实例化后传入;前面传入4次,休眠一秒是为了让任务正常添加到线程池中,调用线程池的shutdown方法,此时未执行完和队列中未执行的任务都将继续执行,但是无法添加新任务,准备销毁线程池,所以后面的任务无法添加,会报错。

1.2 shutdownNow()方法的使用

  1. 创建threadpoolexecutor.myrunnable包,再创建线程类MyRunnable6

    package threadpoolexecutor.myrunnable;
    
    public class MyRunnable6 implements Runnable{
        @Override
        public void run() {
            System.out.println(" begin"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
            for (int i = 0; i < Integer.MAX_VALUE/4; i++) {
                Math.random();
            }
            System.out.println(" end"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
        }
    }
    
    
  2. 创建threadpoolexecutor.run包,再创建类Test1

    package threadpoolexecutor.run;
    
    import threadpoolexecutor.myrunnable.MyRunnable6;
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class Test1 {
        public static void main(String[] args) throws InterruptedException {
            MyRunnable6 myRunnable6 = new MyRunnable6();
            ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
            executor.execute(myRunnable6);
            executor.execute(myRunnable6);
            executor.execute(myRunnable6);
            Thread.sleep(1000);
            executor.shutdownNow();
            executor.execute(myRunnable6);
            System.out.println("main end!");
        }
    }
    
    

    MyRunnable6不能使用Thread.sleep方法,否则调用shutdownNow方法时,会因为冲突报错。
    Test1启动类中因为使用LinkedBlockingQueue队列,所以MaximumPoolSize和keepAliveTime参数是无效的。核心线程数量为2,即线程池只会创建2个线程,其余的全部存入队列中。添加3个任务,其中2个正在执行,1个存入队列中,此时调用shutdownNow()方法,未执行完的任务继续执行,队列中未执行的任务不再执行,以及后面的添加任务被拒绝执行(报错)。
    此时shutdownNow方法的返回值保存的是队列中未执行的任务。

1.3 shutdown()和shutdownNow()方法的中断

  1. 创建threadpoolexecutor.myrunnable包,再创建线程类MyRunnableA

    package threadpoolexecutor.myrunnable;
    
    public class MyRunnableA implements Runnable{
        private String username;
        public MyRunnableA(String username)
        {
            this.username = username;
        }
    
        public String getUsername() {
            return username;
        }
    
        @Override
        public void run() {
            try {
                while(true)
                {
                    if(Thread.currentThread().isInterrupted() == true)
                        throw new InterruptedException();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("---任务名称:"+username+"被中断!");
            }
        }
    }
    
    
  2. 创建threadpoolexecutor.run包,再创建类Test2

    package threadpoolexecutor.run;
    
    import threadpoolexecutor.myrunnable.MyRunnableA;
    
    import java.util.List;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class Test2 {
        public static void main(String[] args) throws InterruptedException {
            MyRunnableA myRunnableA = new MyRunnableA("A1");
            ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
            executor.execute(myRunnableA);
            executor.execute(myRunnableA);
            executor.execute(myRunnableA);
            Thread.sleep(1000);
            //executor.shutdown();
            //将队列中未执行的任务名字打印出来。
            List<Runnable> list = executor.shutdownNow();
            for (int i = 0; i < list.size(); i++) {
                MyRunnableA m1 = (MyRunnableA)list.get(i);
                System.out.println(m1.getUsername());
            }
            System.out.println("main end!");
        }
    }
    
    

    MyRunnableA类中,是一个循环等待当前线程被中断后报错,否则等于死循环。
    启动类Test2中,如果使用shutdown()方法关闭线程池,则会等待未执行完的任务执行完,以及队列中的任务执行完,再关闭线程池,此时不会产生中断操作。
    而使用shutdownNow()方法,则会等待未执行完的任务执行完,但是队列中的任务不再执行并返回,再关闭线程池,此时会产生中断操作。

2、isTerminating()和isTerminated()

1)public boolean isTerminating(),如果处于shutdown()或shutdownNow()之后正在终止但并未完全终止的过程中,也就是还有任务正在执行,则返回true。即是否正在销毁线程池的过程中。
2)public boolean isTerminated(),如果处于shutdown()或shutdownNow()之后并且任务都已经完成,此时返回true。即线程池是否已经销毁。

而isShutdown()判断线程池关闭的命令(shutdown()或shutdownNow())是否已经发出,返回boolean值。

  1. 创建threadpoolexecutor.myrunnable包,再创建线程类MyRunnable7

    package threadpoolexecutor.myrunnable;
    
    public class MyRunnable7 implements Runnable{
        @Override
        public void run() {
            try {
                System.out.println(" begin"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
                Thread.sleep(2000);
                System.out.println(" end"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    
  2. 创建threadpoolexecutor.run包,再创建类Test3

    package threadpoolexecutor.run;
    
    import threadpoolexecutor.myrunnable.MyRunnable7;
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class Test3 {
        public static void main(String[] args) throws InterruptedException {
            MyRunnable7 myRunnable7 = new MyRunnable7();
            ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
            executor.execute(myRunnable7);
            executor.execute(myRunnable7);
            executor.execute(myRunnable7);
            System.out.println(executor.isTerminating()+" - "+executor.isTerminated());
            executor.shutdown();
            Thread.sleep(2000);
            System.out.println(executor.isTerminating()+" - "+executor.isTerminated());
            Thread.sleep(2000);
            System.out.println(executor.isTerminating()+" - "+executor.isTerminated());
            Thread.sleep(1000);
            System.out.println(executor.isTerminating()+" - "+executor.isTerminated());
            System.out.println("main end!");
        }
    }
    
    

    MyRunnable7类中,打印begin和end的标志,中间休眠2秒。
    启动类Test3中,使用shutdown()方法关闭线程池,此时因为未执行完的任务和队列中未执行的任务都会继续执行,又因为核心线程数为2,有3个任务会将第三个放进队列中,所以一共需要等待4秒。
    在调用shutdown()方法之后,分4次调用并在中间休眠,到第4次时耗时5秒,因为此时任务已经执行完毕,所以isTerminated()方法为true,第1,2,3次为false。
    而第4次之前,因为任务还未执行完毕,此时isTerminating()方法为true,第4次为false。

3、ThreadFactory+Thread+UncaughtExceptionHandler处理异常

使用ThreadFactory对线程池中创建的线程属性进行定制化;如果线程出现异常,可以结合UncaughtExceptionHandler处理。

  1. 创建threadpoolexecutor.myrunnable包,再创建线程类MyRunnable8

    package threadpoolexecutor.myrunnable;
    
    public class MyRunnable8 implements Runnable{
        @Override
        public void run() {
            System.out.println(" begin"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
            int b = 3/0;
            System.out.println(" end"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
        }
    }
    
    
  2. 创建threadpoolexecutor.mythreadfactory包,再创建线程工厂类MyThreadFactoryA

    package threadpoolexecutor.mythreadfactory;
    
    import java.util.Date;
    import java.util.concurrent.ThreadFactory;
    
    public class MyThreadFactoryA implements ThreadFactory {
    
        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            thread.setName(" 自定义线程  "+new Date());
            thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
                @Override
                public void uncaughtException(Thread t, Throwable e) {
                    System.out.println("自定义处理异常:"+t.getName()+" "+e.getMessage());
                    e.printStackTrace();
                }
            });
            return thread;
        }
    }
    
    
  3. 创建threadpoolexecutor.run包,再创建类Test5

    package threadpoolexecutor.run;
    
    import threadpoolexecutor.myrunnable.MyRunnable8;
    import threadpoolexecutor.mythreadfactory.MyThreadFactoryA;
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class Test5 {
        public static void main(String[] args) throws InterruptedException {
            MyRunnable8 myRunnable8 = new MyRunnable8();
            //1、使用构造方法设置自定义线程工厂类。
            //ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),new MyThreadFactoryA());
    
            //2、使用setThreadFactory方法设置自定义线程工厂类。
            ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
            executor.setThreadFactory(new MyThreadFactoryA());
            executor.execute(myRunnable8);
        }
    }
    
    

    MyRunnable8打印begin和end标志并在中间制造异常。
    MyThreadFactoryA线程工厂类对创建的线程进行属性定制化,并且设置自定义的异常处理。
    Test5启动类则是创建线程池并添加线程工厂类MyThreadFactoryA,然后执行MyRunnable8实例任务。因为MyRunnable8线程类出现异常,此时会被MyThreadFactoryA捕获并进行异常处理。

4、set/getRejectedExecutionHandler()方法

public void setRejectedExecutionHandler()和public RejectedExecutionHandler setRejectedExecutionHandler()方法的作用是可以处理任务被拒绝执行时的行为。

  1. 创建threadpoolexecutor.myrunnable包,再创建线程类MyRunnable9

    package threadpoolexecutor.myrunnable;
    
    public class MyRunnable9 implements Runnable{
        private String username;
    
        public MyRunnable9(String username) {
            this.username = username;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        @Override
        public void run() {
            try {
                System.out.println(" begin"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
                Thread.sleep(2000);
                System.out.println(" end"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    
  2. 创建threadpoolexecutor.myrejectedexecutionhandler包,再创建类MyRejectedExecutionHandler

    package threadpoolexecutor.myrejectedexecutionhandler;
    
    import threadpoolexecutor.myrunnable.MyRunnable9;
    
    import java.util.concurrent.RejectedExecutionHandler;
    import java.util.concurrent.ThreadPoolExecutor;
    
    public class MyRejectedExecutionHandler implements RejectedExecutionHandler {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            System.out.println( ((MyRunnable9)r).getUsername()+" 被拒绝执行");
        }
    }
    
    
  3. 创建threadpoolexecutor.run包,再创建类Test6

    package threadpoolexecutor.run;
    
    import threadpoolexecutor.myrejectedexecutionhandler.MyRejectedExecutionHandler;
    import threadpoolexecutor.myrunnable.MyRunnable9;
    
    import java.util.concurrent.SynchronousQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class Test6 {
        public static void main(String[] args) {
            MyRunnable9 test1 = new MyRunnable9("test1");
            MyRunnable9 test2 = new MyRunnable9("test2");
            MyRunnable9 test3 = new MyRunnable9("test3");
            ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 100, TimeUnit.SECONDS, new SynchronousQueue<>());
            executor.setRejectedExecutionHandler(new MyRejectedExecutionHandler());
            executor.execute(test1);
            executor.execute(test2);
            executor.execute(test3);
        }
    }
    
    

    创建线程任务类MyRunnable9。
    创建拒绝执行处理器类MyRejectedExecutionHandler,即拒绝执行时,在这个类中进行捕获并处理。
    启动类Test6,创建线程池,参数线程池最大数为2并且队列使用SynchronousQueue<>(),此时同时执行3个任务,第三个任务将会被拒绝执行。该操作会被MyRejectedExecutionHandler类捕获并处理。

5、allowsCoreThreadTimeOut()和allowCoreThreadTimeOut(boolean)方法

public void allowCoreThreadTimeOut(true)可使核心线程池中的空闲线程具有超时销毁的特性。
public boolean allowsCoreThreadTimeOut()方法的作用是判断是否具有以上方法的特性。

  1. 创建threadpoolexecutor.myrunnable包,再创建类MyRunnable10

    package threadpoolexecutor.myrunnable;
    
    public class MyRunnable10 implements Runnable{
    
        @Override
        public void run() {
                System.out.println(" begin"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
                System.out.println(" end"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
        }
    }
    
    
  2. 创建threadpoolexecutor.run包,再创建类Test7

    package threadpoolexecutor.run;
    import threadpoolexecutor.myrunnable.MyRunnable10;
    
    import java.util.concurrent.SynchronousQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class Test7 {
        public static void main(String[] args) throws InterruptedException {
    
            ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3, 5, TimeUnit.SECONDS, new SynchronousQueue<>());
            executor.allowCoreThreadTimeOut(true);
            System.out.println(executor.allowsCoreThreadTimeOut());
            for (int i = 0; i < 2; i++) {
                MyRunnable10 test1 = new MyRunnable10();
                executor.execute(test1);
            }
            Thread.sleep(6000);
            System.out.println(executor.getPoolSize());
        }
    }
    
    

    即在线程池中设置keepAliveTime参数,用以超时销毁线程,但默认不销毁核心线程池;设置allowCoreThreadTimeOut(true)后,当核心线程池中的线程空闲并超过该时间,也会被销毁。

6、prestartCoreThread()和prestartAllCoreThreads()方法

在实例化线程池ThreadPoolExecutor后,可以使用prestartCoreThread()和prestartAllCoreThreads()方法创建核心线程。
public boolean prestartCoreThread()的作用是每调用一次就创建一个核心线程并使它变成启动状态,返回值类型为boolean,代表是否创建成功。
public int prestartAllCoreThreads()的作用是创建并启动所有核心线程,返回值时启动的核心线程数量。

  1. 创建threadpoolexecutor.run包,再创建类Test8
    package threadpoolexecutor.run;
    
    
    import java.util.concurrent.SynchronousQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class Test8 {
        public static void main(String[] args) throws InterruptedException {
    
            ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 5, 5, TimeUnit.SECONDS, new SynchronousQueue<>());
            //prestartCoreThread方法
            System.out.println("线程池中的线程数:"+executor.getPoolSize());
            System.out.println("1="+executor.prestartCoreThread());
    
            System.out.println("线程池中的线程数:"+executor.getPoolSize());
            System.out.println("2="+executor.prestartCoreThread());
    
            //prestartAllCoreThreads方法
            System.out.println("线程池中的线程数:"+executor.getPoolSize());
            System.out.println("创建的核心线程数:"+executor.prestartAllCoreThreads());
    
            //当核心线程满了,调用prestartCoreThread和prestartAllCoreThreads方法,无法创建核心线程
            //prestartCoreThread方法创建时,返回false
            
            System.out.println("线程池中的线程数:"+executor.getPoolSize());
            System.out.println("3="+executor.prestartCoreThread());
    
            System.out.println("线程池中的线程数:"+executor.getPoolSize());
            
            //prestartAllCoreThreads方法创建时,返回0
            System.out.println("创建的核心线程数:"+executor.prestartAllCoreThreads());
            System.out.println("线程池中的线程数:"+executor.getPoolSize());
    
    
    
        }
    }
    
    
    即使用prestartCoreThread()方法创建2个核心线程,再使用prestartAllCoreThreads()创建所有核心线程,因为该线程池设置只有4个核心线程数,所以只能创建2个,返回2。
    后面再使用prestartCoreThread()方法创建核心线程时,因为核心线程池满了,无法创建,返回false;使用prestartAllCoreThreads()创建所有核心线程时,返回0。

7、getCompletedTaskCount()方法

public long getCompletedTaskCount()的作用是取得已经执行完成的任务总数。

  1. 创建threadpoolexecutor.run包,再创建类Test9

    package threadpoolexecutor.run;
    
    import java.util.concurrent.*;
    
    public class Test9 {
        public static void main(String[] args) throws InterruptedException {
            Runnable runnable = new Runnable() {
    
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                        System.out.println("输出"+Thread.currentThread().getName());
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            };
            ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, 5, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
            
            executor.execute(runnable);
            executor.execute(runnable);
            executor.execute(runnable);
            executor.execute(runnable);
            executor.execute(runnable);
    
            Thread.sleep(1000);
            System.out.println(executor.getCompletedTaskCount());
            Thread.sleep(1000);
            System.out.println(executor.getCompletedTaskCount());
            Thread.sleep(1000);
            System.out.println(executor.getCompletedTaskCount());
            Thread.sleep(1000);
            System.out.println(executor.getCompletedTaskCount());
            Thread.sleep(1000);
            System.out.println(executor.getCompletedTaskCount());
        }
    }
    
    

    创建线程池每次只能同时执行2个任务,此时有5个任务,第一次打印为0,第二次为2,第三次为4,第四次为5,第五次还是5。

四、线程池ThreadPoolExecutor的拒绝策略及其他方法

1、ThreadPoolExecutor的拒绝策略

1.1 AbortPolicy策略(默认策略)

AbortPolicy策略是当任务添加到线程池中被拒绝时,将跑出RejectedExcutionException异常,这是线程池默认使用的拒绝策略。

1.2 CallerRunsPolicy策略

CallerRunsPolicy策略是当任务添加到线程池中被拒绝时,会让调用者线程处理被拒绝的任务,此时调用者线程会被阻塞(等待线程池被拒绝的任务处理完毕)。

  1. 创建threadpoolexecutor.thread包,再创建类MyRunnable

    package threadpoolexecutor.thread;
    
    public class MyRunnable extends Thread{
        @Override
        public void run() {
            try {
                Thread.sleep(5000);
                System.out.println("end " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    
  2. 创建threadpoolexecutor.thread包,再创建类MyThreadA

    package threadpoolexecutor.thread;
    
    import java.util.concurrent.LinkedBlockingDeque;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class MyThreadA extends Thread{
        @Override
        public void run() {
            MyRunnable myRunnable = new MyRunnable();
            LinkedBlockingDeque queue = new LinkedBlockingDeque<>(2);
            ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3, 5, TimeUnit.SECONDS, queue, new ThreadPoolExecutor.CallerRunsPolicy());
            System.out.println("myThreadA begin "+Thread.currentThread().getName()+ " "+System.currentTimeMillis());
            executor.execute(myRunnable);
            executor.execute(myRunnable);
            executor.execute(myRunnable);
            executor.execute(myRunnable);
            executor.execute(myRunnable);
            executor.execute(myRunnable);
    
        }
    }
    
    
  3. 创建threadpoolexecutor.run包,再创建类Run10

    package threadpoolexecutor.run;
    
    import threadpoolexecutor.thread.MyThreadA;
    
    import java.util.concurrent.LinkedBlockingDeque;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class Run10 {
        public static void main(String[] args) {
            MyThreadA myThreadA = new MyThreadA();
            myThreadA.setName("A");
            myThreadA.start();
            System.out.println("main "+Thread.currentThread().getName()+ " "+System.currentTimeMillis());
        }
    }
    
    

    MyRunnable是一个任务类。
    MyThreadA是一个线程池操作类,负责创建并添加任务,也就是调用者类。创建的线程池的队列容量为2,并且最大线程数为3,此时添加6个任务,第六个任务被调用者处理并使该调用者类线程阻塞。
    Run10是启动类,用来实例化MyThreadA这个调用者类。让MyThreadA处理线程池,是因为该策略会阻塞调用者线程,这样做可以使main线程不被阻塞,实现继续向下执行任务,从而不会影响程序的运行效率。

1.3 DiscardOldestPolicy策略

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

  1. 创建threadpoolexecutor.myrunnable包,再创建任务类MyRunnable11

    package threadpoolexecutor.myrunnable;
    
    public class MyRunnable11 implements Runnable{
        private String username;
        public MyRunnable11(String username){
            this.username = username;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        @Override
        public void run() {
            try {
                System.out.println(" username="+username+" begin");
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    
  2. 创建threadpoolexecutor.run包,再创建类Run11

    package threadpoolexecutor.run;
    
    import threadpoolexecutor.myrunnable.MyRunnable11;
    
    import java.util.Iterator;
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class Run11 {
        public static void main(String[] args) throws InterruptedException {
            ArrayBlockingQueue queue = new ArrayBlockingQueue(2);
            ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3, 5, TimeUnit.SECONDS, queue, new ThreadPoolExecutor.DiscardOldestPolicy());
            for (int i = 0; i < 5; i++) {
                MyRunnable11 myRunnable11 = new MyRunnable11("Runnable" + (i + 1));
                executor.execute(myRunnable11);
            }
            Thread.sleep(1000);
            Iterator iterator = queue.iterator();
            while(iterator.hasNext())
            {
                Object o = iterator.next();
                System.out.println(((MyRunnable11)o).getUsername());
            }
            System.out.println("任务3,4会因为以下两个任务被丢弃");
            executor.execute(new MyRunnable11("Runnable6"));
            executor.execute(new MyRunnable11("Runnable7"));
            iterator = queue.iterator();
            while(iterator.hasNext())
            {
                Object o = iterator.next();
                System.out.println(((MyRunnable11)o).getUsername());
            }
        }
    }
    
    

    创建任务类MyRunnable11。
    创建Run11启动类,类中创建一个ThreadPoolExecutor线程池类,拒绝策略为DiscardOldestPolicy,即将等待队列中队头任务丢弃。并且该线程的最大线程池数为3,队列容量为2,也就是最大并发是5个,超过5个会启动拒绝策略。即第一次打印队列为任务3和4,后面因为加入任务6,任务3被丢弃;加入任务7,任务4被丢弃,此时队列中只有任务6和7。

1.4 DiscardPolicy策略

DiscardPolicy策略是当任务添加到线程池中被拒绝时,线程池丢弃被拒绝的任务。

  1. 创建threadpoolexecutor.run包,再创建类Run12
    package threadpoolexecutor.run;
    
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class Run12 {
        public static void main(String[] args) throws InterruptedException {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(5000);
                        System.out.println(Thread.currentThread().getName()+" end");
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            };
    
            ArrayBlockingQueue queue = new ArrayBlockingQueue(2);
            ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3, 5, TimeUnit.SECONDS, queue, new ThreadPoolExecutor.DiscardPolicy());
            executor.execute(runnable);
            executor.execute(runnable);
            executor.execute(runnable);
            executor.execute(runnable);
            executor.execute(runnable);
            executor.execute(runnable);
            executor.execute(runnable);
            Thread.sleep(6000);
            System.out.println(executor.getPoolSize()+ " - "+queue.size());
        }
    }
    
    
    创建Run12启动类,类中创建一个ThreadPoolExecutor线程池类,拒绝策略为DiscardPolicy,即将被拒绝的任务丢弃。并且该线程的最大线程池数为3,队列容量为2,也就是最大并发是5个,超过5个会启动拒绝策略。即加入任务6和7时,超过最大并发数,此时被拒绝执行的任务6和7会被丢弃。
    每个任务都要休眠5秒,最多同时执行3个任务,任务3和4在等待队列中等待,任务6和7被拒绝执行,此时第六秒打印线程池线程数量和队列容量,因为前3个任务(1,2,5)已经执行完,第六秒时已经在执行任务3和4了,即队列容量为0,但是因为线程存活时间为5秒,所以线程池中的线程数量还是3。

2、afterExecute()和beforeExecute()方法

线程池中的这2个方法可以对线程池中执行的线程对象实现监控。

  1. 创建threadpoolexecutor.myrunnable包,再创建任务类MyRunnable12

    package threadpoolexecutor.myrunnable;
    
    public class MyRunnable12 implements Runnable{
        private String username;
        public MyRunnable12(String username){
            this.username = username;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName()+" username="+username+" begin"+System.currentTimeMillis());
                Thread.sleep(3000);
                System.out.println(Thread.currentThread().getName()+" username="+username+" end"+System.currentTimeMillis());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    
  2. 创建threadpoolexecutor.executor包,再创建类MyThreadPoolExecutor

    package threadpoolexecutor.executor;
    
    import threadpoolexecutor.myrunnable.MyRunnable12;
    
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class MyThreadPoolExecutor extends ThreadPoolExecutor {
        public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
        }
    
        @Override
        protected void afterExecute(Runnable r, Throwable t) {
            super.afterExecute(r, t);
            System.out.println(((MyRunnable12)r).getUsername()+" 已执行完!");
        }
    
        @Override
        protected void beforeExecute(Thread t, Runnable r) {
            super.beforeExecute(t, r);
            System.out.println(((MyRunnable12)r).getUsername()+" 准备执行!");
        }
    }
    
    
  3. 创建threadpoolexecutor.run包,再创建类Run13

    package threadpoolexecutor.run;
    
    import threadpoolexecutor.executor.MyThreadPoolExecutor;
    import threadpoolexecutor.myrunnable.MyRunnable12;
    
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.LinkedBlockingDeque;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class Run13 {
        public static void main(String[] args) throws InterruptedException {
            MyThreadPoolExecutor executor = new MyThreadPoolExecutor(2, 2, 5, TimeUnit.SECONDS, new LinkedBlockingDeque());
    
            executor.execute(new MyRunnable12("1"));
            executor.execute(new MyRunnable12("2"));
            executor.execute(new MyRunnable12("3"));
            executor.execute(new MyRunnable12("4"));
        }
    }
    
    

    创建任务类MyRunnable12。
    创建线程池类MyThreadPoolExecutor,重写beforeExecute和afterExecute方法从而实现对线程对象的监控。
    创建启动类Run13,实例化自定义线程类MyThreadPoolExecutor,添加任务MyRunnable12实例。此时在执行任务前会打印准备执行,在执行任务后会打印执行结束。可以用以具体操作。

3、remove(Runnable)方法

public boolean remove(Runnable)方法可以删除一个未被执行的Runnable任务。

  1. 创建threadpoolexecutor.run包,再创建类Test10

    package threadpoolexecutor.run;
    
    import java.util.concurrent.LinkedBlockingDeque;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class Test10 {
        public static void main(String[] args) throws InterruptedException {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(Thread.currentThread().getName()+" begin");
                        Thread.sleep(3000);
                        System.out.println(Thread.currentThread().getName()+" end");
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            };
            ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 5, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
            executor.execute(runnable);
            executor.execute(runnable);
            Thread.sleep(1000);
            executor.remove(runnable);
    
        }
    }
    
    
  2. 创建threadpoolexecutor.run包,再创建类Test11

    package threadpoolexecutor.run;
    
    import java.util.concurrent.LinkedBlockingDeque;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class Test11 {
        public static void main(String[] args) throws InterruptedException {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(Thread.currentThread().getName()+" begin");
                        Thread.sleep(3000);
                        System.out.println(Thread.currentThread().getName()+" end");
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            };
            ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 5, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
            executor.submit(runnable);
            executor.submit(runnable);
            Thread.sleep(1000);
            executor.remove(runnable);
    
        }
    }
    
    

    Test10启动类中,添加任务用的方法是线程池的execute方法,未执行的任务可以用remove(Runnable)移除。
    Test11启动类中,添加任务用的方法是线程池的submit方法,未执行的任务不能用remove(Runnable)移除。

4、ThreadPoolExecutor线程池的get方法

  1. public int getActiveCount()方法,作用是取得线程池中正在执行任务的线程的个数 。
  2. public int getCorePoolSize()方法,作用是取得ThreadPoolExecutor线程池通过构造方法传入的corePoolSize参数值。
  3. public int MaximumPoolSize()方法,作用是取得ThreadPoolExecutor线程池通过构造方法传入的MaximumPoolSize参数值。
  4. public long getTaskCount()方法,作用是取得ThreadPoolExecutor线程池中添加的任务个数。getCompletedTaskCount()方法,获取已完成的任务的个数。
  5. public int getLargestPoolSize()方法,作用是返回线程池中存活线程数最多时的线程数。

总结

  1. Executor的子接口是ExecutorService,ExecutorService的唯一子实现类AbstractExecutorService,AbstractExecutorService的子类ThreadPoolExecutor。
  2. 使用Executors工厂类的newCachedThreadPool()方法创建无界线程池,并且进行复用线程池中线程,只需要创建的线程闲置后即复用。
    使用newCacheThreadPool(ThreadFactory)方法定制线程工厂,如果使用无参方法,则采用默认的线程工厂DefaultThreadFactory。否则采用传入定制的线程工厂。
    使用newCachedThreadPool()方法创建无界线程池的缺点,在高并发情况下,容易造成内存占用率大幅升高,导致内存溢出或者系统运行效率严重下降。因为线程可以无界限的被创建。
    使用Executors工厂类的newFixedThreadPool(int)创建有界线程池,即线程池中能创建的线程对象的最大值为指定数量。
    使用Executors工厂类的newSingleThreadExecutor()创建单一线程池。
    newCacheThreadPool()创建的线程池在理论上可以创建并存放无限个线程对象;newFixedThreadPool(int)创建的线程池可以创建并存放指定数量的线程对象;newSingleThreadExecutor()创建的线程池可以创建并存放一个线程对象。
  3. 当线程池中现有的线程小于等于corePoolSize时,keepAliveTime参数无效,因为线程不会被清除。大于corePoolSize时,超过指定时间keepAliveTime后,大于corePoolSize的那部分空闲线程会被清除。
    在超过核心线程数后,当线程空闲后,超过过期时间,则该空闲线程会被清除,仅保留核心线程数量的线程。
    超过设置的线程池最大值+队列容量的数值时,无法继续添加任务,该任务被拒绝执行。
  4. public void shutdown()的作用是使当前未执行完的任务继续执行,而队列中未执行的任务继续执行,不删除队列中的任务,不再允许添加新任务,同时shutdown()方法不会阻塞,不会中断线程池中的线程。
    public List shutdownNow()的作用是使当前未执行完的任务继续执行,而队列中未执行的任务不再执行,删除队列中的任务并返回,不再允许添加新任务,同时shutdownNow()方法不会阻塞,会中断线程池中的线程。
    即这两个方法的区别在于队列的任务会不会继续执行以及删除。
    这两个方法是用来销毁线程池的。
    isShutdown()判断线程池关闭的命令是否已经发出,返回boolean值。
    public boolean isTerminating(),判断线程池是否正在销毁的过程中。
    public boolean isTerminated(),判断线程池是否已经销毁。
    public boolean awaitTermination(long timeout,TimeUnit unit),作用是查看在指定的时间内,线程池是否已经终止工作。如果在指定时间内,线程池销毁会到使该方法不再阻塞,而超过timeout的时间也会使该方法不再阻塞,否则一直处于阻塞状态。
  5. ThreadFactory+Thread+UncaughtExceptionHandler处理异常,即Thread出现异常,会被ThreadFactory类设置的UncaughtExceptionHandler捕获到,并进行处理。同时ThreadFactory可以对Thread线程类进行属性定制化。
    public void setRejectedExecutionHandler()和public RejectedExecutionHandler setRejectedExecutionHandler()方法的作用是可以处理任务被拒绝执行时的行为。即被拒绝执行时的一些处理操作。
    public void allowCoreThreadTimeOut(true)可使核心线程池中的空闲线程具有超时销毁的特性。即需要销毁核心线程池的时候可以设置该属性。
    public boolean allowsCoreThreadTimeOut()方法的作用是判断是否具有以上方法的特性。
    在实例化线程池ThreadPoolExecutor后,可以使用prestartCoreThread()和prestartAllCoreThreads()方法创建核心线程。
    public long getCompletedTaskCount()的作用是取得已经执行完成的任务总数。
  6. 线程池的拒绝策略有AbortPolicy(默认)、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy4种。
    默认策略AbortPolicy,即报错;
    CallerRunsPolicy策略,即让执行者线程处理被拒绝执行的任务,从而使该执行者线程无法执行后面的程序,等待拒绝执行的任务处理完成后,再继续执行后面的程序;
    DiscardOldestPolicy策略,即将等待队列中的队头任务丢弃,从而加入被拒绝执行的任务;
    DiscardPolicy策略,即将被拒绝执行的任务丢弃。
    afterExecute()和beforeExecute()方法,即在任务执行的前后插入分别一个处理,从而实现对线程对象的监控。
    remove(Runnable)方法每次只能移除一个使用execute方法添加的任务。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值