代码改变世界

java学习:线程池和异步

2016-03-06 23:20  14174  阅读(2910)  评论(0编辑  收藏  举报

1.异步和同步

同步执行很容易理解,代码的操作顺序就是程序执行的顺序。但是实际使用中,很多场景常常会受限于同步执行,不能充分利用cpu的资源,例如,要查找一大批数据中的最大数,同步执行时,可能是花费10单位的时间读取数据,1单位的时间进行计算,总计在11单位时间后得到结果;而,异步执行时,分派10个线程执行任务,将会花费1单位的时间读取数据,1单位时间进行计算,总计在2单位时间后得到结果。

相对于同步而言,异步本质上是申请线程,提高cpu的利用率(单核cpu执行计算密集型任务时会降低)更快地得到结果。在解决问题时合理地选择同步和异步能更好地利用好计算资源。

从数据的角度来看,在同步操作中,数据的变化都保持一定的先后顺序关系,不会出现冲突的情况;而在异步操作中,数据随时可能被其中某个线程更改,这时需要注意数据的一致性,尤其是写操作,要保证事务性,这里可以对数据加锁来实现。另外有时线程之间也需要保证一定的顺序,需要使用线程锁(这里有的可以通过编码技巧或者回调方法解决)。

 

2.线程池

在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁。如何利用已有对象来服务就是一个需要解决的关键问题,其实这就是一些"池化资源"技术产生的原因。比如大家所熟悉的数据库连接池正是遵循这一思想而产生的,接下来将介绍的线程池技术同样符合这一思想。

在java中,可以使用java.util.concurrent.ThreadPoolExecutor来创建线程池,

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

创建方法中的几个参数需要注意:

先说最重要的3个参数,corePoolSize,maximumPoolSize,workQueue。corePoolSize设定核心线程数,maximumPoolSize设定最大线程数,workQueue设定等待队列。当有新任务时,首先检查当前的线程数是否小于corePoolSize,如果是,则新建线程处理任务;如果不是,再检查当前workQueue是否已满,如果未满,则把新的任务加入workQueue,core线程完成时会从workQueue中取得任务继续执行;如果workQueue已满,再检查当前线程数是否小于maximumPoolSize,如果是,则创建线程处理任务,如果不是,则抛出拒绝服务的异常(默认是抛出异常,具体如何处理是最后一个参数RejectExecutionHandler来决定的)。

其他的参数分别是,keepAliveTime、unit控制空闲线程的存活时间;threadFactory,创建新的线程时使用的创建者;handler,拒绝服务时的处理方式;(后两个参数都有默认的选择)

 

从线程池的工作方式可以看到,core,max,queue决定了线程池的表现,下面讲述这三个参数的参考设定。

一般来说,需要处理的qps平均为n,最大为m,每个请求的处理时间为t(秒)时,core=nt+,max=mt+,queue视需要而定(当这些请求需要尽快响应,cpu资源常有空闲时,queue=0)

对于cpu密集型任务,如大量数据的计算,匹配,排序等,这时cpu的处理能力成为瓶颈,core和max要注意不要设定得太大,要衡量好cpu的处理能力。

对于io密集型任务,如操作数据库,http请求等,core和max可以考虑设定得更大,因为线程通常处于等待之中,不会耗费多少cpu。

(20160329.add)对于cpu密集型任务:cpu为单核时,没有必要使用多线程:单线程时,cpu资源主要都在计算上;多线程时,cpu还需要额外耗费线程之间切换的资源,降低了计算效率。cpu为多核时,有必要使用多线程(单线程时只有一个核在进行计算)

——总的来说,当cpu常有空闲的情况时,就应该考虑使用多线程了。

 

一些使用的例子:

1.简单的一个线程

public class TestClass {
    /*/*/
    private SettableFuture<String> settableFuture = SettableFuture.create();
    public void todo(final String param) throws Exception{
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(100);
                    System.out.println("begin " + param);
                    Thread.sleep(1000);
                    System.out.println("finish sleep");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    settableFuture.set("complete " + param);
                }
            }
        });
        thread.start();
    }

    public SettableFuture<String> getSettableFuture() {
        return settableFuture;
    }

    public static void main(String[] args) throws Exception {
        TestClass testClass = new TestClass();
        testClass.todo("test");
        System.out.println("start todo");
        System.out.println(testClass.getSettableFuture().get());
    }
}

 

2.线程池&异步回调

public class TestClass {
    private ListenableFuture<String> listenableFuture;
    private ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));

    public void todo(final String param){
        listenableFuture = listeningExecutorService.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                System.out.println("call " + param);
                Thread.sleep(100);
                return "call " + param + " complete";
            }
        });

        Futures.addCallback(listenableFuture, new FutureCallback<String>() {

            @Override
            public void onSuccess(String s) {
                try {
                    System.out.println(s + " success");
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onFailure(Throwable throwable) {
                System.out.println("failed");
            }
        });
    public static void main(String[] args) throws InterruptedException {
        TestClass testClass = new TestClass();
        testClass.todo("test");
        System.out.println("ok");
    }
}

 

3.同时执行多个异步回调任务,等待所有任务结束后,输出所有任务的执行结果

public class TestClass {
    private ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(5));
    public void todo(final String param, final CountDownLatch countDownLatch, final List<String> result) throws InterruptedException {
            ListenableFuture listenableFuture = listeningExecutorService.submit(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    Thread.sleep(100);
                    System.out.println("exec " + param);
                    result.add(String.valueOf(param));
                    System.out.println("exec "+param+" finished");
                    return String.valueOf(param);
                }
            });
            Futures.addCallback(listenableFuture, new FutureCallback<String>() {
                @Override
                public void onSuccess(String s) {
                    System.out.println("success "+s);
                    countDownLatch.countDown();
                }

                @Override
                public void onFailure(Throwable throwable) {
                    System.out.println("failed");
                    countDownLatch.countDown();
                }
            });
        }
    public static void main(String[] args) throws InterruptedException {
        int taskSize = 4;
        TestClass testClass = new TestClass();
        final List<String> result = Lists.newArrayList();
        final CountDownLatch countDownLatch = new CountDownLatch(taskSize);

        for (int i = 0; i < taskSize; i++) {
            testClass.todo("test" + i, countDownLatch, result);
        }

        System.out.println("add task finished");
        countDownLatch.await(10, TimeUnit.SECONDS);
        System.out.println(result);
        testClass.listeningExecutorService.shutdown();
    }
    //*/
}