zoukankan      html  css  js  c++  java
  • Java中线程池的学习

     

       线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。

      用线程池来管理的好处是,可以保证系统稳定运行,适用与有大量线程,高工作量的情景下使用,假如要展示1000张图片如果创建1000个线程去加载,保证系统会死掉。用线程池就可以避免这个问题,可以用5个线程轮流执行,5个一组,执行完的线程不直接回收而是等待下次执行,这样对系统的开销就可以减小不少。

    ===============================Executor的译文部分==================================

    ExecutorJava工具类,执行提交给它的Runnable任务。该接口提供了一种基于任务运行机制的任务提交方法,包括线程使用详细信息,时序等等。Executor通常用于替代创建多线程。例如:你可能会使用以下方式来代替创建线程集合中的线程newThread(new(RunnableTask())).start()

     

     Executor executor = anExecutor;
     executor.execute(new RunnableTask1());
     executor.execute(new RunnableTask2());
     ...

    尽管如此,Executor接口没有明确要求执行过程是异步的。举个最简单的例子,一个Executor可以在调用者的线程中运行提交的任务。

     class DirectExecutor implements Executor {
         public void execute(Runnable r) {
             r.run();
         }
     }

    更典型的是,任务也可以运行在其他的线程而不是调用者线程。以下代码就是在Executor中生成新的线程。

    class ThreadPerTaskExecutor implements Executor {
         public void execute(Runnable r) {
             new Thread(r).start();
         }
     }

    很多Executor的实现按照任务的实现方式和时间来分类,下面的代码将提交的任务序列化给第二个Executor,阐述了一个组合的Executor。

    class SerialExecutor implements Executor {
       final Queue tasks = new ArrayDeque();
       final Executor executor;
       Runnable active;
    
       SerialExecutor(Executor executor) {
         this.executor = executor;
       
       public synchronized void execute(final Runnable r) {
         tasks.offer(new Runnable() {
           public void run() {
             try {
               r.run();
             } finally {
               scheduleNext();
             }
           }
         });
         if (active == null) {
           scheduleNext();
         }
       }
    
       protected synchronized void scheduleNext() {
         if ((active = tasks.poll()) != null) {
           executor.execute(active);
         }
       }
     }}

    以上代码简答讲就是执行一个SerialExecutor时,先执行Runnablerun(),然后再从Tasks任务堆栈中找到当前激活的任务并执行。

    在这个package包中实现的Executor实现了ExecutorService,它是个扩展接口。

    threadPoolExecutor类提供了一个扩展的线程池实现。Executors类给这些Executors提供了方便的工程方法。

    内存一致性效果:在提交一个Runnable对象给Executor执行之前,线程中的行为可能先在另一个线程中发生。

    ===============================Executor的译文部分==================================

    Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService

    根据线程池的执行策略,Executorexecute()可能在新线程中执行,或者在线程池中的某个线程中执行,也可能是在调用者线程中执行。ExecutorServiceExecutor的基础上增加了两个核心方法:

    1Future<?>submit(Runnable task)

    2<T>Future<T> submit(Callable<T> task)

    差异点:这两个方法都可以向线程池提交任务,区别在于Runnable执行完run()有返回值,而Callable执行完call()后有返回值。

    共同点:submit都返回Future对象,Future对象可以阻塞线程直到运行完毕,也可以取消任务执行和检测任务是否执行完毕。

    executors类里面提供了一些静态工厂,生成一些常用的线程池:

    1newSingleThreadExecutor创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

    2newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

    3newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

    4newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

    5newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。

    下面再介绍下ThreadPoolExecutor类,以便对线程池有进一步认识:

    ThreadPoolExecutor(int corePoolSize, 

                       int maximumPoolSize,
                       long keepAliveTime, 

                       TimeUnit unit,
                       BlockingQueue<Runnable> workQueue,
                       RejectedExecutionHandler handler)
    corePoolSize   :线程池维护线程的最少数量
    maximumPoolSize:线程池维护线程的最大数量
    keepAliveTime  :线程池维护线程所允许的空闲时间
    unit           :线程池维护线程所允许的空闲时间的单位
    workQueue      :线程池所使用的缓冲队列
    handler        :线程池对拒绝任务的处理策略

    当一个任务通过execute(Runnable)方法欲添加到线程池时:

    1如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
    2
    、如果此时线程池中的数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列。
    3
    、如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
    4
    、如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过handler所指定的策略来处理此任务。

    也就是说,处理任务的优先级为:
    核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。

     

    简单的例子:

    ThreadPoolTestMain.java

    package threadpool.test;
    
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    
    public class ThreadPoolTestMain {
    	private static final int CORE_POOL_SIZE = 2;
    	private static final int MAX_POOL_SIZE = 4;
    	private static final int KEEP_ACTIVE_TIME = 3;
    	private static final int TASK_NUM = 10;
    	private static final int PRODUCE_SLEEP_TIME = 10;
    	
    	static public void main(String[] args) {
    		// 构造一个线程池  
            ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE, 
    								MAX_POOL_SIZE, 
    								KEEP_ACTIVE_TIME, 
    								TimeUnit.SECONDS, 
    								new ArrayBlockingQueue<Runnable>(3),  
    								new ThreadPoolExecutor.DiscardOldestPolicy());
            
            for (int i = 1; i < TASK_NUM; i++) {
            	String name = "Task" + i;
            	try {
            		System.out.println("ThreadPoolTestMain: put a task: " + name);
            		threadPool.execute(new ThreadPoolTask(name));
            		Thread.sleep(20);
            	} catch (Exception err) {
            		err.printStackTrace();
            	}
            }
        }
    }

    ThreadPoolTask.java

    package threadpool.test;
    
    public class ThreadPoolTask implements Runnable {
    	private String mTaskName;
    	private static int CONSUME_SLEEP_TIME = 2000;
    	
    	public ThreadPoolTask(String name) {
    		mTaskName = name;
    	}
    	
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		System.out.println(Thread.currentThread().getName());
    		System.out.println("ThreadPoolTask :" + mTaskName);
    		
            try {
                Thread.sleep(CONSUME_SLEEP_TIME);
            } catch (Exception err) {  
                err.printStackTrace();
            }
        }
    	
    }

    执行结果:

    ThreadPoolTestMain:put a task: Task1

    pool-1-thread-1

    ThreadPoolTask:Task1

    ThreadPoolTestMain:put a task: Task2

    pool-1-thread-2

    ThreadPoolTask:Task2

    ThreadPoolTestMain:put a task: Task3

    ThreadPoolTestMain:put a task: Task4

    ThreadPoolTestMain:put a task: Task5

    ThreadPoolTestMain:put a task: Task6

    pool-1-thread-3

    ThreadPoolTask:Task6

    ThreadPoolTestMain:put a task: Task7

    pool-1-thread-4

    ThreadPoolTask:Task7

    ThreadPoolTestMain:put a task: Task8

    ThreadPoolTestMain:put a task: Task9

    pool-1-thread-1

    ThreadPoolTask:Task5

    pool-1-thread-2

    ThreadPoolTask:Task8

    pool-1-thread-3

    ThreadPoolTask:Task9

     

  • 相关阅读:
    pig实战 pig常用语法总结,教你快速入门——算法篇
    本科生码农应该会的6种基本排序算法(《数据结构与算法》)
    java 大块内存做数据缓存 大数据的高效收发
    一键安装zookeeper脚本制作篇 相关经验浅谈
    C语言第01次作业顺序、分支结构
    C语言 第三次作业函数
    hashmap笔记
    ArrayList排序与对象的序列化
    插入排序笔记(MIT算法导论课程)
    java解析四则运算表达式
  • 原文地址:https://www.cnblogs.com/riskyer/p/3341894.html
Copyright © 2011-2022 走看看