[JAVA][线程池]

1.线程池理解

线程池在系统启动时即创建大量空闲的线程,程序将一个Runnable对象传给线程池,线程池就会启动一条线程来执行该对象的run方法,当run方法执行结束后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个Runnable对象的run方法。
线程池是为突然大量爆发的线程设计的,通过有限的几个固定线程为大量的操作服务,减少了创建和销毁线程所需的时间,从而提高效率。
线程池用于并发运行若干个执行时间不长且互不干预的任务。在JDK 1.5的java.lang.concurrent包里提供了实现线程池的方法。

2.线程池结构分析

1)线程池的生成

实现线程池的主要接口是ExecutorService,他实现了Executor接口, ExecutorService是通过ThreadPoolExecutor类实例化一个线程池的。而Executors是生成各种线程池的工厂。

//Executors工厂里生成CachedThreadPool线程池的一个方法    
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

可以看出来,在Executors工厂里,通过ThreadPoolExecutor实例化了一个线程池。并返回ExecutorService 。
还有一种计划线程池,这种线程池主要用于定时任务和固定周期的重复任务,也就是ScheduledExecutorService,这个接口继承了ExecutorService ,这种线程池也是通过Executors工厂实例化的,在Executors工厂里,这种线程池是通过ScheduledThreadPoolExecutor来实例化的,ScheduledThreadPoolExecutor是继承了ThreadPoolExecutor类。

//Executors工厂里生产的计划任务
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

所以我们知道有两种线程池,分别是ExecutorService 和ScheduledExecutorService,而ScheduledExecutorService是继承了ExecutorService 。
(1)ExecutorService ,这个接口有四种线程池,分别是newCachedThreadPool,newFixedThreadPool,SingleThreadExecutor和newWorkStealingPool。
(2)ScheduledExecutorService有两种线程池,分别是newScheduledThreadPool和newSingleThreadScheduledExecutor。

2)执行任务

ExecutorService 执行任务有三个方法,分别是execute,submit和invoke。而ScheduledExecutorService,还有一个schedule方法。
1)execute

 void execute(Runnable command);

接收 java.lang.Runnable 对象作为参数,并且以异步的方式执行它,执行这个方法是没有返回值的。
2)submit

Future<?> submit(Runnable task);
<T> Future<T> submit(Callable<T> task);

submit可以执行Runable和Callable的任务,会返回一个Future对象。
3)invoke

    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException;
    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException;

invoke只执行Callable任务,有两个关于invoke的方法,分别是invokeAny和invokeAll。
4)schedule

    public ScheduledFuture<?> schedule(Runnable command,
                                       long delay, TimeUnit unit);
    public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                           long delay, TimeUnit unit);

这个方法是ScheduledExecutorService里的,也能执行Runable和Callable任务。

3)中断任务

线程池是可以中断的通过shutdown和shutdownNow。
(1)shutdown()
当线程池调用该方法时,线程池的状态则立刻变成SHUTDOWN状态。此时,则不能再往线程池中添加任何任务,否则将会抛出RejectedExecutionException异常。但是,此时线程池不会立刻退出,直到添加到线程池中的任务都已经处理完成,才会退出。
(2)shutdownNow()
根据JDK文档描述,大致意思是:执行该方法,线程池的状态立刻变成STOP状态,并试图停止所有正在执行的线程,抛出java.io.InterruptedIOException异常不再处理还在池队列中等待的任务,当然,它会返回那些未执行的任务。它试图终止线程的方法是通过调用Thread.interrupt()方法来实现的,但是大家知道,这种方法的作用有限,如果线程中没有sleep 、wait、Condition、定时锁等应用, interrupt()方法是无法中断当前的线程的。所以,ShutdownNow()并不代表线程池就一定立即就能退出,它可能必须要等待所有正在执行的任务都执行完成了才能退出。

3.例子解析

1)ExecutorService

(1)newCachedThreadPoo

/**
 * @author Administrator 线程池的作用:
 * 
 *         线程池是为突然大量爆发的线程设计的,通过有限的几个固定线程为大量的操作服务,减少了创建和销毁线程所需的时间,从而提高效率。
 * 
 *         如果一个线程的时间非常长,就没必要用线程池了(不是不能作长时间操作,而是不宜。),况且我们还不能控制线程池中线程的开始、挂起、和中止。
 *         
 *         线程池为无限大,newCachedThreadPool 只会重用空闲并且可用的线程,如果没有空闲的可用线程,就只能不停地创建新线程,这样可能会造成OOM
 */
public class TestnewCachedThreadPool extends Activity {

    private ExecutorService executorService = null;
    private String path = Environment.getExternalStorageDirectory()+"/lgyThreadPool/CachedThreadPool/";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test);
        executorService = Executors.newCachedThreadPool();
        executorService.submit(new DownTask("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1206/18/c0/12043463_1339987116996.jpg", path, "img1.jpg"));
        executorService.submit(new DownTask("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1209/13/c0/13829920_1347512303112.jpg", path, "img2.jpg"));
        executorService.submit(new DownTask("http://img5.imgtn.bdimg.com/it/u=376657532,1073811657&fm=23&gp=0.jpg", path, "img3.jpg"));
        executorService.submit(new DownTask("http://www.shusp.com/wp-content/uploads/2015/11/4vAcI1TdZh3D07tWjctIFQvv5r1.jpg", path, "img4.jpg"));
    }
}

(2)newFixedThreadPool

/**
 * FixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
 */
public class TestnewFixedThreadPool extends Activity {

    private Button btn_cancel = null;
    private ExecutorService executorService = null;
    private String path = Environment.getExternalStorageDirectory()
            + "/lgyThreadPool/FixedThreadPool/";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test);
        executorService = Executors.newFixedThreadPool(2);
        executorService.submit(new DownTask("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1206/18/c0/12043463_1339987116996.jpg", path, "img1.jpg"));
        executorService.submit(new DownTask("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1209/13/c0/13829920_1347512303112.jpg", path, "img2.jpg"));
        executorService.submit(new DownTask("http://img5.imgtn.bdimg.com/it/u=376657532,1073811657&fm=23&gp=0.jpg", path, "img3.jpg"));
        executorService.submit(new DownTask("http://www.shusp.com/wp-content/uploads/2015/11/4vAcI1TdZh3D07tWjctIFQvv5r1.jpg", path, "img4.jpg"));
    
        btn_cancel = (Button) findViewById(R.id.button1);
        btn_cancel.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {

                if (executorService!=null) {
                    //当线程池调用该方法时,线程池的状态则立刻变成SHUTDOWN状态。此时,则不能再往线程池中添加任何任务,
                    //否则将会抛出RejectedExecutionException异常。但是,此时线程池不会立刻退出,
                    //直到添加到线程池中的任务都已经处理完成,才会退出。
                    //executorService.shutdown();
                    
                    //根据JDK文档描述,大致意思是:执行该方法,线程池的状态立刻变成STOP状态,并试图停止所有正在执行的线程,抛出java.io.InterruptedIOException异常
                    //不再处理还在池队列中等待的任务,当然,它会返回那些未执行的任务。 
                    //它试图终止线程的方法是通过调用Thread.interrupt()方法来实现的,但是大家知道,这种方法的作用有限,
                    //如果线程中没有sleep 、wait、Condition、定时锁等应用, interrupt()方法是无法中断当前的线程的。
                    //所以,ShutdownNow()并不代表线程池就一定立即就能退出,它可能必须要等待所有正在执行的任务都执行完成了才能退出。
                    executorService.shutdownNow();
                }
            }
        });
    }
}

(3)newSingleThreadExecutor

/**
 * SingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
 * 
 */
public class TestnewSingleThreadExecutor extends Activity {

    private Button btn_cancel = null;
    private ExecutorService executorService = null;
    private String path = Environment.getExternalStorageDirectory()+"/lgyThreadPool/SingleThreadExecutor/";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test);
        
        executorService = Executors.newSingleThreadExecutor();
        
        executorService.submit(new DownTask("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1206/18/c0/12043463_1339987116996.jpg", path, "img1.jpg"));
        executorService.submit(new DownTask("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1209/13/c0/13829920_1347512303112.jpg", path, "img2.jpg"));
        executorService.submit(new DownTask("http://img5.imgtn.bdimg.com/it/u=376657532,1073811657&fm=23&gp=0.jpg", path, "img3.jpg"));
        executorService.submit(new DownTask("http://www.shusp.com/wp-content/uploads/2015/11/4vAcI1TdZh3D07tWjctIFQvv5r1.jpg", path, "img4.jpg"));

        
        //shutdown()可以让循环的任务终止
        btn_cancel = (Button) findViewById(R.id.button1);
        btn_cancel.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {

                if (executorService!=null) {
                    
                    executorService.shutdown();
                }
            }
        });
    }
}

(4)newWorkStealingPool

/**
 * WorkStealingPool,任务窃取线程池,可以实例化一个ForkJoinPool对象,这个线程池主要适合将大任务分成小任务来解决的。
 * 创建一个线程池,保持足够的线程支持并行性水平,并可能使用多个队列减少争用。
 */
public class TestnewWorkStealingPool extends Activity {

    private Button btn_cancel = null;
    private ExecutorService executorService = null;
    private ForkJoinPool forkJoinPool = null;
    private String path = Environment.getExternalStorageDirectory()+"/lgyThreadPool/WorkStealingPool/";
    @TargetApi(24) @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test);
        
        executorService = Executors.newWorkStealingPool();
        executorService.submit(new DownTask("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1206/18/c0/12043463_1339987116996.jpg", path, "img1.jpg"));
        executorService.submit(new DownTask("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1209/13/c0/13829920_1347512303112.jpg", path, "img2.jpg"));
        executorService.submit(new DownTask("http://img5.imgtn.bdimg.com/it/u=376657532,1073811657&fm=23&gp=0.jpg", path, "img3.jpg"));
        executorService.submit(new DownTask("http://www.shusp.com/wp-content/uploads/2015/11/4vAcI1TdZh3D07tWjctIFQvv5r1.jpg", path, "img4.jpg"));
        
        //shutdown()可以让循环的任务终止
        btn_cancel = (Button) findViewById(R.id.button1);
        btn_cancel.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {

                if (executorService!=null) {
                    
                    executorService.shutdown();
                }
            }
        });
    }
}

2)ScheduledExecutorService

(1)newScheduledThreadPool

/**
 * ScheduledThreadPool核心线程数量固定,当非核心线程闲置时立即回收,主要用于定时任务和固定周期的重复任务
 * 
 */
public class TestnewScheduledThreadPool extends Activity {

    private Button btn_cancel = null;
    private ScheduledExecutorService executorService = null;
    private String path = Environment.getExternalStorageDirectory()+"/lgyThreadPool/ScheduledThreadPool/";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test);
        
        executorService = Executors.newScheduledThreadPool(2);
        
        //schedule(Runnable command, long delay, TimeUnit unit)
        //schedule延时执行任务
        //unit设置的是TimeUnit.SECONDS,即单位是秒,延时delay秒执行command任务
//      executorService.schedule(new DownTask("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1206/18/c0/12043463_1339987116996.jpg", path, "img1.jpg"), 1, TimeUnit.SECONDS);
//      executorService.schedule(new DownTask("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1209/13/c0/13829920_1347512303112.jpg", path, "img2.jpg"), 1, TimeUnit.SECONDS);
//      executorService.schedule(new DownTask("http://img5.imgtn.bdimg.com/it/u=376657532,1073811657&fm=23&gp=0.jpg", path, "img3.jpg"), 1, TimeUnit.SECONDS);
//      executorService.schedule(new DownTask("http://www.shusp.com/wp-content/uploads/2015/11/4vAcI1TdZh3D07tWjctIFQvv5r1.jpg", path, "img4.jpg"), 1, TimeUnit.SECONDS);
    
        //scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
        //scheduleAtFixedRate执行延时并有重复动作的任务
        //unit设置的是TimeUnit.SECONDS,即单位是秒,第一次延时initialDelay秒执行,之后每period秒重复执行,如果上一次执行的时间超过了period秒,那么当上一次任务执行完成后将直接执行下一次任务,而不需再等待period秒再执行
//      executorService.scheduleAtFixedRate(new DownTask("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1206/18/c0/12043463_1339987116996.jpg", path, "img1.jpg"), 10, 1, TimeUnit.SECONDS);
        
        //scheduleWithFixedDelay(command, initialDelay, delay, unit)
        //scheduleWithFixedDelay执行延时并有重复动作的任务,但这个重复执行任务是阻塞的,只有执行完上一次任务,才会继续执行下一次任务
        //unit设置的是TimeUnit.SECONDS,即单位是秒,第一次延时initialDelay秒执行,只有当上一次任务执行完成后才会执行下一次,(上一次执行完成后,延时delay秒执行)然后重复执行括号里的动作
//      executorService.scheduleWithFixedDelay(new DownTask("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1206/18/c0/12043463_1339987116996.jpg", path, "img1.jpg"), 1, 1, TimeUnit.SECONDS);
        
        //shutdown()可以让循环的任务终止
        btn_cancel = (Button) findViewById(R.id.button1);
        btn_cancel.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {

                if (executorService!=null) {
                    
                    executorService.shutdown();
                }
            }
        });
    }
}

(2)newSingleThreadScheduledExecutor

/**
 * SingleThreadScheduledExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行,
 * 他在SingleThreadExecutor的功能基础上,还能实现定时任务和固定周期的重复任务。
 * 
 */
public class TestnewSingleThreadScheduledExecutor extends Activity {

    private Button btn_cancel = null;
    private ScheduledExecutorService executorService = null;
    private String path = Environment.getExternalStorageDirectory()+"/lgyThreadPool/SingleThreadScheduledExecutor/";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test);
        
        executorService = Executors.newSingleThreadScheduledExecutor();
        
//      executorService.submit(new DownTask("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1206/18/c0/12043463_1339987116996.jpg", path, "img1.jpg"));
//      executorService.submit(new DownTask("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1209/13/c0/13829920_1347512303112.jpg", path, "img2.jpg"));
//      executorService.submit(new DownTask("http://img5.imgtn.bdimg.com/it/u=376657532,1073811657&fm=23&gp=0.jpg", path, "img3.jpg"));
//      executorService.submit(new DownTask("http://www.shusp.com/wp-content/uploads/2015/11/4vAcI1TdZh3D07tWjctIFQvv5r1.jpg", path, "img4.jpg"));

        Log.i("lgy", "SingleThreadExecutor========");
        executorService.schedule(new DownTask("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1206/18/c0/12043463_1339987116996.jpg", path, "img1.jpg"), 10, TimeUnit.SECONDS);
        executorService.schedule(new DownTask("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1209/13/c0/13829920_1347512303112.jpg", path, "img2.jpg"), 10, TimeUnit.SECONDS);
        executorService.schedule(new DownTask("http://img5.imgtn.bdimg.com/it/u=376657532,1073811657&fm=23&gp=0.jpg", path, "img3.jpg"), 2, TimeUnit.SECONDS);
        executorService.schedule(new DownTask("http://www.shusp.com/wp-content/uploads/2015/11/4vAcI1TdZh3D07tWjctIFQvv5r1.jpg", path, "img4.jpg"), 2, TimeUnit.SECONDS);
        //executorService.schedule运行结果:
//              03-30 15:34:56.050: I/lgy(25106): SingleThreadExecutor========
//              03-30 15:34:58.090: I/lgy(25106): img3.jpg is downloaded start
//              03-30 15:34:58.110: I/lgy(25106): img3.jpg is downloaded finish
//              03-30 15:34:58.200: I/lgy(25106): img4.jpg is downloaded start
//              03-30 15:34:58.470: I/lgy(25106): img4.jpg is downloaded finish
//              03-30 15:35:06.150: I/lgy(25106): img1.jpg is downloaded start
//              03-30 15:35:09.240: I/lgy(25106): img1.jpg is downloaded finish
//              03-30 15:35:09.490: I/lgy(25106): img2.jpg is downloaded start
//              03-30 15:35:13.510: I/lgy(25106): img2.jpg is downloaded finish

        
        //shutdown()可以让循环的任务终止
        btn_cancel = (Button) findViewById(R.id.button1);
        btn_cancel.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {

                if (executorService!=null) {
                    
                    executorService.shutdown();
                }
            }
        });
    }
}

4.参考文章

http://blog.csdn.net/lonelyroamer/article/details/7993637
http://blog.csdn.net/ns_code/article/details/17465497
http://justsee.iteye.com/blog/999189

5.源码地址

http://download.csdn.net/detail/lgywsdy/9815653

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,064评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,606评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,011评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,550评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,465评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,919评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,428评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,075评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,208评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,185评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,191评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,914评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,482评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,585评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,825评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,194评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,703评论 2 339

推荐阅读更多精彩内容