ExecutorService实现线程池

来源:互联网 发布:linux cal如何实现 编辑:程序博客网 时间:2024/06/01 21:07

ExecutorService是java自带线程池

线程池的作用:

线程池作用就是限制系统中执行线程的数量。
根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。

为什么要用线程池:

  1. 减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
  2. 可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。
    Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。

比较重要的几个类:

ExecutorService 真正的线程池接口。 ScheduledExecutorService 能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。 ThreadPoolExecutor ExecutorService的默认实现。 ScheduledThreadPoolExecutor 继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。

要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。
1. newSingleThreadExecutor
创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
2. newFixedThreadPool
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
3. newCachedThreadPool
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,
那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
4. newScheduledThreadPool
创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
详见:http://www.oschina.net/question/565065_86540

线程安全与非线程安全的类:

  • StringBuffer 是同步的 synchronized append();
  • StringBuilder 不是同步的 append();
    相对而言StringBuffer在处理上稍逊于StringBuilder,但是其是线程安全的。当不存在并发时首选应当使用StringBuilder。
    同样的:
  • Vector 和 Hashtable 是线程安全的而ArrayList 和 HashMap则不是线程安全的。
  • 对于集合而言,Collections提供了几个静态方法,可以将集合或Map转换为线程安全的:
    例如:
    Collections.synchronizedList() :获取线程安全的List集合
    Collections.synchronizedMap():获取线程安全的Map
...    List<String> list = new ArrayList<String>();    list.add("A");    list.add("B");    list.add("C");    list = Collections.synchronizedList(list);//将ArrayList转换为线程安全的集合    System.out.println(list);//[A,B,C]   可以看出,原集合中的元素也得以保留    ...

使用ExecutorService实现线程池

当一个程序中若创建大量线程,并在任务结束后销毁,会给系统带来过度消耗资源,以及过度切换线程的危险,从而可能导致系统崩溃。为此我们应使用线程池来解决这个问题。
ExecutorService是java提供的用于管理线程池的类。
线程池有两个主要作用:

  • 控制线程数量
  • 重用线程

线程池的概念:首先创建一些线程,它们的集合称为线程池,当服务器接受到一个客户请求后,就从线程池中取出一个空闲的线程为之服务,服务完后不关闭该线程,而是将该线程还回到线程池中。
在线程池的编程模式下,任务是提交给整个线程池,而不是直接交给某个线程,线程池在拿到任务后,它就在内部找有无空闲的线程,再把任务交给内部某个空闲的线程,任务是提交给整个线程池,一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务
线程池有以下几种实现策略:
- Executors.newCachedThreadPool()
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。
- Executors.newFixedThreadPool(int nThreads)
创建一个可重用固定线程集合的线程池,以共享的无界队列方式来运行这些线程。
- Executors.newScheduledThreadPool(int corePoolSize)
创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
- Executors.newSingleThreadExecutor()
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
可以根据实际需求来使用某种线程池。例如,创建一个有固定线程数量的线程池:

 ...    ExecutorService threadPool         = Executors.newFixedThreadPool(30);//创建具有30个线程的线程池    Runnable r1 = new Runable(){        public void run(){            //线程体        }    };    threadPool.execute(r1);//将任务交给线程池,其会分配空闲线程来运行这个任务。    ...

使用BlockingQueue

BlockingQueue是双缓冲队列。
在多线程并发时,若需要使用队列,我们可以使用Queue,但是要解决一个问题就是同步,但同步操作会降低并发对Queue操作的效率。
BlockingQueue内部使用两条队列,可允许两个线程同时向队列一个做存储,一个做取出操作。在保证并发安全的同时提高了队列的存取效率。
双缓冲队列有一下几种实现:
- ArrayBlockingDeque:规定大小的BlockingDeque,其构造函数必须带一个int参数来指明其大小.其所含的对象是以FIFO(先入先出)顺序排序的。
- LinkedBlockingDeque:大小不定的BlockingDeque,若其构造函数带一个规定大小的参数,生成的BlockingDeque有大小限制,若不带大小参数,所生成的BlockingDeque的大小由Integer.MAX_VALUE来决定.其所含的对象是以FIFO(先入先出)顺序排序的。
- PriorityBlockingDeque:类似于LinkedBlockDeque,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数的Comparator决定的顺序。
- SynchronousQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成的。
例如:

    public static void main(String[] args) {        BlockingQueue<String> queue            = new LinkedBlockingDeque<String>();        try {            //queue.offer("A");//立即向队列末尾追加元素            /*             * 向队列末尾追加元素,指定可延迟5秒。             * 若5秒钟内成功将元素加入队列返回true             * 若超时后元素仍然没有加入队列则返回flase             */            queue.offer("A",5,TimeUnit.SECONDS);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println(queue.poll());    }

案例

案例1:测试使用ExecutorService实现线程池

详细要求如下:
1. 线程池要执行的任务为每隔一秒输出一次当前线程的名字,总计输出10次。
2. 创建一个线程池,该线程池中只有两个空线程。
3. 使线程池执行5次步骤一的任务。

import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * 测试线程池 */public class TestExecutorService {    public static void main(String[] args) {        ExecutorService threadPool = Executors.newFixedThreadPool(2);        for(int i=0;i<5;i++){            Handler handler = new Handler();            threadPool.execute(handler);        }    }}class Handler implements Runnable{    public void run(){        String name = Thread.currentThread().getName();        System.out.println("执行当前任务的线程为:"+name);        for(int i=0;i<10;i++){                    System.out.println(name+":"+i);            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                e.printStackTrace();            }        }        System.out.println(name+":任务完毕");    }}

案例2:测试BlockingQueue的使用

详细要求如下:
1. 首先,使用ArrayBlockingQueue类创建一个大小为10的双缓冲队列queue;然后,循环20次向队列queue中添加元素,如果5秒内元素仍没有入队到队列中,则返回false。
2. 首先,使用ArrayBlockingQueue类创建一个大小为10的双缓冲队列queue;然后,将0到9,10个数字加入到队列queue中;最后,循环20次从队列queue中取元素,如果5秒内还没有元素可取出则返回null。

import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.BlockingQueue;import java.util.concurrent.TimeUnit;import org.junit.Test;/** * 测试双缓冲队列 */public class TestBlockingQueue {    /**     * 测试入队方法     */    @Test    public void testOffer() {        BlockingQueue<Integer> queue             = new ArrayBlockingQueue<Integer>(10);        for(int i=0;i<20;i++){            try {                //设置5秒超时,5秒内元素仍没有入队,则返回false                boolean b = queue.offer(i,5,TimeUnit.SECONDS);                System.out.println("存入是否成功:"+b);            } catch (InterruptedException e) {                e.printStackTrace();            }        }        }    /**     * 测试出队方法     */    @Test    public void testPull() {        BlockingQueue<Integer> queue             = new ArrayBlockingQueue<Integer>(10);        for(int i=0;i<10;i++){            queue.offer(i);        }            for(int i =0;i<20;i++){            //获取元素,设置5秒超时,5秒内还没有元素可取出则返回null            try {                Integer num = queue.poll(5, TimeUnit.SECONDS);                System.out.println("元素:"+num);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}
0 0
原创粉丝点击