• 线程池ExecutorService


      说到java开发,免不了跟多线程打交道。Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。因此,在Java 5之后,通过Executor来启动线程比使用Thread的start方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,还有关键的一点:有助于避免this逃逸问题——如果我们在构造器中启动一个线程,因为另一个任务可能会在构造器结束之前开始执行,此时可能会访问到初始化了一半的对象用Executor在构造器中。

    ExecutorService,是一个接口,继承了Executor;

    Executor,只有一个方法:

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

    Executors提供了4中创建线程池:

    public static ExecutorService newCachedThreadPool(); 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 

    public static ExecutorService newFixedThreadPool(int nThreads); 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 创建一个定长线程池,支持定时及周期性任务执行。 

    public static ExecutorService newSingleThreadExecutor(); 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

    通过Executors的以上四个静态工厂方法获得 ExecutorService实例,而后调用该实例的execute(Runnable command)方法即可。一旦Runnable任务传递到execute()方法,该方法便会自动在一个线程上执行。

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class ExecutorTest {
        public static void main(String[] args){
            ExecutorService executorService = Executors.newCachedThreadPool();   
            //ExecutorService executorService = Executors.newFixedThreadPool(3);  
    //      ExecutorService executorService = Executors.newSingleThreadExecutor(); 
            for (int i = 0; i < 3; i++){ 
                RunnableTest method = new RunnableTest();
                executorService.execute(method);   
                System.out.println("########" + i + "########");   
            }   
            executorService.shutdown(); 
        }
    }
    public class RunnableTest implements Runnable{   
        public void run(){   
            System.out.println(Thread.currentThread().getName() + "线程执行");   
        }  
    }

    输出结果:

    ########0########
    ########1########
    ########2########
    pool-1-thread-2线程执行
    pool-1-thread-3线程执行
    pool-1-thread-1线程执行

    任务分两类:一类是实现了Runnable接口的类,一类是实现了Callable接口的类。两者都可以被ExecutorService执行,但是Runnable任务没有返回值,而Callable任务有返回值。并且Callable的call()方法只能通过ExecutorService的submit(Callable<T> task) 方法来执行,并且返回一个 <T>Future<T>,是表示任务等待完成的 Future。

    下面给出一个Executor执行Callable任务的示例代码

    public class ExecutorTest {
        public static void main(String[] args){
            ExecutorService executorService = Executors.newCachedThreadPool();   
            //ExecutorService executorService = Executors.newFixedThreadPool(3);  
    //      ExecutorService executorService = Executors.newSingleThreadExecutor(); 
            List<Future<String>> resultList = new ArrayList<Future<String>>();  
            for (int i = 0; i < 6; i++){ 
                CallableTest method = new CallableTest(i);
                Future<String> future = executorService.submit(method);   
                System.out.println("########" + i + "########");   
                resultList.add(future);
            }   
    
            //遍历任务的结果   
            for (Future<String> fs : resultList){   
                    try{   
                        //Future返回如果没有完成,则一直循环等待,直到Future返回完成  
                        while(!fs.isDone());
                        //打印各个线程(任务)执行的结果 
                        System.out.println(fs.get());     
                    }catch(InterruptedException e){   
                        e.printStackTrace();   
                    }catch(ExecutionException e){   
                        e.printStackTrace();   
                    }finally{   
                        //启动一次顺序关闭,执行以前提交的任务,但不接受新任务  
                        executorService.shutdown();   
                    }   
            }   
        }
    }
    import java.util.concurrent.Callable;
    
    public class CallableTest implements Callable<String>{
        
        private int id; 
        
        public CallableTest(int id){   
            this.id = id;   
        }  
        
        @Override
        public String call() throws Exception {
            System.out.println(Thread.currentThread().getName() + "线程执行");  
            return Thread.currentThread().getName() + "线程执行" + "返回id=" + id;
        }  
    }

    输出结果:

    pool-1-thread-1线程执行返回id=0
    pool-1-thread-2线程执行返回id=1
    pool-1-thread-3线程执行返回id=2
    pool-1-thread-4线程执行返回id=3
    pool-1-thread-2线程执行返回id=4
    pool-1-thread-4线程执行返回id=5

    从结果中可以同样可以看出,submit也是首先选择空闲线程来执行任务,如果没有,才会创建新的线程来执行任务。另外,需要注意:如果Future的返回尚未完成,则get()方法会阻塞等待,直到Future完成返回,可以通过调用isDone()方法判断Future是否完成了返回。

  • 相关阅读:
    打印一个0到1之间的数的二进制表示
    bzoj-3223 文艺平衡树
    Linux下使用fstatfs/statfs查询系统相关信息
    Pku3664
    hdu-2544 最短路
    java同步包种ArrayBlockingQueue类的分析与理解
    【剑指offer】不用加减乘除做加法
    PA模块经常使用表
    网络编程
    // 插入排序 源代码
  • 原文地址:https://www.cnblogs.com/cjingzm/p/6055509.html
Copyright © 2020-2023  润新知