Concurrent - CompletionService

原创转载请注明出处:http://agilestyle.iteye.com/blog/2343752

 

CompletionService的功能是以异步的方式一边生产新的任务,一边处理已完成任务的结果,这样可以将执行任务与处理任务分离开来进行处理。使用submit()执行任务,使用take()取得已完成的任务,并按照完成这些任务的时间顺序处理它们的结果。这样就可以很好的解决Future接口的get()方法的阻塞特性带来的性能问题。

Future的缺点

MyCallable.java

package org.fool.java.concurrent.completionservice;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<String> {
    private String threadName;
    private Integer sleepTime;

    public MyCallable(String threadName, Integer sleepTime) {
        this.threadName = threadName;
        this.sleepTime = sleepTime;
    }

    @Override
    public String call() throws Exception {
        System.out.println(threadName);
        Thread.sleep(sleepTime);
        return "return " + threadName;
    }
}

CompletionServiceTest0.java

package org.fool.java.concurrent.completionservice;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class CompletionServiceTest0 {
    public static void main(String[] args) {
        MyCallable thread1 = new MyCallable("thread1", 5000);
        MyCallable thread2 = new MyCallable("thread2", 4000);
        MyCallable thread3 = new MyCallable("thread3", 3000);
        MyCallable thread4 = new MyCallable("thread4", 2000);
        MyCallable thread5 = new MyCallable("thread5", 1000);

        List<Callable> callableList = new ArrayList<>();
        callableList.add(thread1);
        callableList.add(thread2);
        callableList.add(thread3);
        callableList.add(thread4);
        callableList.add(thread5);

        ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>());

        List<Future> futureList = new ArrayList<>();

        callableList.forEach(callable -> futureList.add(executor.submit(callable)));

        futureList.forEach(future -> {
            try {
                System.out.println(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });
    }
}

Run


Note:

可以看到,线程1执行的时间耗时最多, 调用get()方法时候一直阻塞到此任务完成时为止,也就是主线程并不能保证首先获得的是最先完成任务的返回值,一旦获取不到,就进入排队等待,大大影响运行效率,这就是Future的缺点。

 

使用CompletionService

CompletionService可以解决Future阻塞同步性的问题。

CompletionServiceTest1.java

package org.fool.java.concurrent.completionservice;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class CompletionServiceTest1 {
    public static void main(String[] args) {
        MyCallable thread1 = new MyCallable("thread1", 5000);
        MyCallable thread2 = new MyCallable("thread2", 4000);
        MyCallable thread3 = new MyCallable("thread3", 3000);
        MyCallable thread4 = new MyCallable("thread4", 2000);
        MyCallable thread5 = new MyCallable("thread5", 1000);

        List<Callable> callableList = new ArrayList<>();
        callableList.add(thread1);
        callableList.add(thread2);
        callableList.add(thread3);
        callableList.add(thread4);
        callableList.add(thread5);

        ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
        CompletionService service = new ExecutorCompletionService(executor);

        callableList.forEach(callable -> service.submit(callable));

        callableList.forEach(i -> {
            try {
                System.out.println(service.take().get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });
    }
}

Run


Note:

CompletionService完全解决了Future阻塞的特性,哪个任务先执行完,哪个任务的返回值就先打印。

但是,在CompletionService接口中如果当前任务被执行完,则service.take().get()方法还是呈阻塞特性。 

 

take()

take()取得最先完成任务的Future对象,谁执行时间最短谁最先返回。

CompletionServiceTest2.java

package org.fool.java.concurrent.completionservice;

import java.util.concurrent.*;

public class CompletionServiceTest2 {
    public static void main(String[] args) {
        try {
            ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
            CompletionService service = new ExecutorCompletionService(executor);

            for (int i = 0; i < 10; i++) {
                service.submit(new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        long sleepTime = (int) (Math.random() * 1000);

                        System.out.println("sleep=" + sleepTime + " " + Thread.currentThread().getName());

                        Thread.sleep(sleepTime);

                        return Thread.currentThread().getName() + " sleep " + sleepTime;
                    }
                });
            }

            for (int i = 0; i < 10; i++) {
                System.out.println(service.take().get());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

Run 


Note:

take()是按任务执行的速度,从快到慢的顺序获得Future对象 

 

poll()

poll()的作用是获取并移除表示下一个已完成的Future,如果不存在这样的任务,则返回null,且无阻塞。

CompletionServiceTest3.java

package org.fool.java.concurrent.completionservice;

import java.util.concurrent.*;

public class CompletionServiceTest3 {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
        CompletionService service = new ExecutorCompletionService(executor);

        service.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(5000);
                System.out.println("after 5 seconds...");
                return Thread.currentThread().getName();
            }
        });

        System.out.println(service.poll());
    }
}

Run


 

poll(long timeout, TimeUnit unit)

poll(long timeout, TimeUnit unit)作用是等待指定的timeout时间,在timeout时间之内获取到值时立即向下继续执行,如果超时也立即向下执行。

MyCallableA.java

package org.fool.java.concurrent.completionservice;

import java.util.concurrent.Callable;

public class MyCallableA implements Callable<String> {
    @Override
    public String call() throws Exception {
        Thread.sleep(5000);
        System.out.println("MyCallableA " + System.currentTimeMillis());
        return "CallableA";
    }
} 

Note:

A睡5秒

MyCallableB.java

package org.fool.java.concurrent.completionservice;

import java.util.concurrent.Callable;

public class MyCallableB implements Callable<String> {
    @Override
    public String call() throws Exception {
        Thread.sleep(10000);
        System.out.println("MyCallableB " + System.currentTimeMillis());
        return "CallableB";
    }
} 

Note:

B睡10秒 

CompletionServiceTest4.java

package org.fool.java.concurrent.completionservice;

import java.util.concurrent.*;

public class CompletionServiceTest4 {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
        CompletionService service = new ExecutorCompletionService(executor);

        service.submit(new MyCallableA());
        service.submit(new MyCallableB());

        for (int i = 0; i < 2; i++) {
            System.out.println(service.poll());
        }

        System.out.println("main end...");
    }
}

Run 


Note:

返回2个null值,因为任务未执行完毕。 

CompletionServiceTest5.java

package org.fool.java.concurrent.completionservice;

import java.util.concurrent.*;

public class CompletionServiceTest5 {
    public static void main(String[] args) {
        try {
            ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
            CompletionService service = new ExecutorCompletionService(executor);

            service.submit(new MyCallableA());
            service.submit(new MyCallableB());

            for (int i = 0; i < 2; i++) {
                System.out.println(service.poll(7, TimeUnit.SECONDS).get());
            }

            System.out.println("main end...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

Note:

设置timeout为7秒

Run


Note:

返回2个值,一共等待了14秒 

CompletionServiceTest6.java

package org.fool.java.concurrent.completionservice;

import java.util.concurrent.*;

public class CompletionServiceTest6 {
    public static void main(String[] args) {
        try {
            ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
            CompletionService service = new ExecutorCompletionService(executor);

            service.submit(new MyCallableA());
            service.submit(new MyCallableB());

            System.out.println("after 4 seconds...");
            System.out.println(service.poll(4, TimeUnit.SECONDS));

            System.out.println("after 6 seconds...");
            System.out.println(service.poll(2, TimeUnit.SECONDS).get());

            System.out.println("after 12 seconds...");
            System.out.println(service.poll(6, TimeUnit.SECONDS).get());

            System.out.println("main end...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

Run 


Note:

第4秒的时候,返回null,因为还未达到5秒,其他2次都打印得到返回值,因为任务已经完成。

所以poll(long timeout, TimeUnit unit)中的timeout的值如果小于任务执行时间,则返回值为null

 

submit(Runnable task, V result)

CompletionServiceTest7.java

package org.fool.java.concurrent.completionservice;

import java.util.concurrent.*;

public class CompletionServiceTest7 {
    public static void main(String[] args) {
        try {
            User user = new User();
            MyThread thread = new MyThread(user);

            ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
            CompletionService service = new ExecutorCompletionService(executor);

            Future<User> future = service.submit(thread, user);

            System.out.println(future.get().getUsername() + ":" + future.get().getPassword());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    public static class User {
        private String username;
        private String password;

        public User() {
        }

        public User(String username, String password) {
            this.username = username;
            this.password = password;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }
    }

    public static class MyThread implements Runnable {

        private User user;

        public MyThread(User user) {
            this.user = user;
        }

        @Override
        public void run() {
            user.setUsername("hello");
            user.setPassword("world");
            System.out.println("my thread invoked...");
        }
    }
}

Run


 

 

 

 

猜你喜欢

转载自agilestyle.iteye.com/blog/2343752