zoukankan      html  css  js  c++  java
  • java线程(5)——线程池(上)

           引入:

           在之前的例子中,我们需要使用线程时就直接去创建一个线程,这样既不浪费资源又十分方便。但如果我们需要创建多个并发的线程,而且短时间执行就结束了,如果还用之前的方式,就会大大降低效率和性能了。

          因此就引入了线程池。

          在java线程池中,涉及到的有Executors、Executor、ExecutorService等,Executor接口表示线程池,ExecutorService负责管理线程池,Executors负责创建生成ExecutorService的实例,提供了线程池的简单实现。他们相互之间的关系如下:


    1、Executor

    public interface Executor {
        void execute(Runnable command);
    }


           在上面的例子中,我们就调用了execute()方法。execute()方法用来执行Runnable类型的任务,它的子接口是ExecutorService。


           ExecutorService负责管理线程池,提供了包括关闭、提交等一系列的操作。

    public interface ExecutorService extends Executor {
    
      
        void shutdown();
    
       
        List<Runnable> shutdownNow();
    
        boolean isShutdown();
    
       
        boolean isTerminated();
    
     //提交
        <T> Future<T> submit(Callable<T> task);
    
       
        <T> Future<T> submit(Runnable task, T result);
    
       
        Future<?> submit(Runnable task);
        
        ......
       
     
    }

    2、Executors

           Executors类负责生成各种类型的线程池的实例,但主要有三种:固定线程池、可变和单任务线程池。其他详细的方法可参见下面:

    public class Executors {
    
    //1、固定大小线程池
        public static ExecutorService newFixedThreadPool(int nThreads) {
            return new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue<Runnable>());
        }
    
    //2、可变尺寸线程池
        public static ExecutorService newCachedThreadPool() {
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                          60L, TimeUnit.SECONDS,
                                          new SynchronousQueue<Runnable>());
        }
    
    //3、单任务线程池
        public static ExecutorService newSingleThreadExecutor() {
            return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1,
                                        0L, TimeUnit.MILLISECONDS,
                                        new LinkedBlockingQueue<Runnable>()));
        }
    
    //4、延迟连接池
        public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
            return new ScheduledThreadPoolExecutor(corePoolSize);
        }
    	
    //5、单任务延迟连接池
        public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
            return new DelegatedScheduledExecutorService
                (new ScheduledThreadPoolExecutor(1));
        }
    }


    我们先通过简单的例子看一下这三种线程池的不同。

    1)固定线程池

    public static void main(String[] args) {
    		// 固定线程池:创建一个线程池,有三个线程
    	 ExecutorService threadPool = Executors.newFixedThreadPool(3);
    	
    		for (int i = 1; i <= 10; i++) {
    			final int task = i;
    			threadPool.execute(new Runnable() {
    
    				@Override
    				public void run() {
    					
    						try {
    							Thread.sleep(20);
    						} catch (InterruptedException e) {
    							e.printStackTrace();
    						}
    						System.out.println(Thread.currentThread().getName()
    								+ "====="  + "for task of" + task);
    				
    				}
    			});
    		}
    		System.out.println("all task have committed");
    		
    		}

    执行结果:



    2)缓存(不固定)线程池

     ExecutorService threadPool=Executors.newCachedThreadPool();

    修改一行代码,创建缓存可变长的线程池。执行结果为



    3)单任务线程池

    ExecutorService threadPool = Executors.newSingleThreadExecutor();

    在该线程池中只有一个线程执行。虽然看起来跟单线程没什么区别,但他的优点是,有替补线程可以随时补上。如果该单线程出现问题,立马就会有一个线程继续执行,安全性大大提高。执行结果如下:



    4)延迟连接池

    // 启动定时器
    		Executors.newScheduledThreadPool(3).scheduleAtFixedRate(new Runnable() {
    
    			@Override
    			public void run() {
    				
    				System.out.println("booming__________"
    						+ getTime());
    
    			}
    		}, 3, 2, TimeUnit.SECONDS);


    其中scheduleAtFixedRate方法参数:

    ScheduledFuture<?> java.util.concurrent.ScheduledExecutorService.scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)


    时间单位为秒,初始延迟为3秒,每隔2秒执行一次。执行结果为:




    使用: 

          1)直接使用Executors

          上面的例子都是直接通过调用Executors中的静态方法实现创建线程池的,很容易理解。

          再看一下具体是如何创建线程池的,以其中一种为例:

    public static ExecutorService newFixedThreadPool(int nThreads) {
            return new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue<Runnable>());
        }


    在ThreadPoolExecutor中,需要传入参数Executors.defaultThreadFactory(),他是默认的线程工厂。

     public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue) {
            this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                 Executors.defaultThreadFactory(), defaultHandler);
        }


    DefaultThreadFactory依旧在Executors类中。

    /**
         * The default thread factory默认线程工厂
         */
        static class DefaultThreadFactory implements ThreadFactory {
            private static final AtomicInteger poolNumber = new AtomicInteger(1);
    		//线程组
            private final ThreadGroup group;
            private final AtomicInteger threadNumber = new AtomicInteger(1);
            private final String namePrefix;
    
    		//构造方法
            DefaultThreadFactory() {
                SecurityManager s = System.getSecurityManager();
                group = (s != null) ? s.getThreadGroup() :
                                      Thread.currentThread().getThreadGroup();
    								  
    			//拼接线程名称:如"pool-1-thread-1"
                namePrefix = "pool-" +
                              poolNumber.getAndIncrement() +
                             "-thread-";
            }
           //重写newThread方法
            public Thread newThread(Runnable r) {
                Thread t = new Thread(group, r,
                                      namePrefix + threadNumber.getAndIncrement(),
                                      0);
    			//如果是守护进程,置为false
                if (t.isDaemon())
                    t.setDaemon(false);
    			//设置默认优先级
                if (t.getPriority() != Thread.NORM_PRIORITY)
                    t.setPriority(Thread.NORM_PRIORITY);
                return t;
            }
        }


    在创建线程池之后,返回的threadpool只有部分参数信息,包括最大线程数、线程名称等,此时只有main主线程。


    当执行threadPool.execute()时,会调用上述的默认线程工厂中的newThread方法,创建线程。


           2)直接使用ThreadPoolExecutor类创建

           这种方式并不推荐使用,因为对于开发者来说比较困难,也不好管理和维护。但这种方式可以做到对线程池更细致更自由化的控制。

           这块内容是线程创建的核心,我们下篇继续介绍。


  • 相关阅读:
    CEPH篇 目录
    kubernetes篇 容器用户权限控制
    深度学习篇-如何理解置信区间
    Mac软件安装篇 for Mac
    JAVA进阶篇 内存模型
    负载均衡篇 不同层次的负载均衡(2/3/4/7)
    NETTY篇 一篇文章看懂NETTY
    base64加密后无法解密
    Spring-Cloud-Gateway 从升级到放弃
    spring gateway 截取response 长度缺失
  • 原文地址:https://www.cnblogs.com/saixing/p/6730219.html
Copyright © 2011-2022 走看看