并发编程指南之定制并发类

Java 并发API提供许多接口和类来实现并发应用程序。它们提供底层(low-level)机制,如Thread类、Runnable或Callable接口、或synchronized关键字。同样也提供高级(high-level)机制,如Executor框架和Java 7 发布的Fork/Join框架。尽管这样,你可能发现你自己开发一个程序时,没有一个java类能满足你的需求。
在这种情况下,你也许需要基于Java提供的(API)实现自己定制的并发工具。基本上,你可以:
实现一个接口提供那个接口定义的功能。比如:ThreadFactory接口。
覆盖一个类的一些方法来调整它的行为以满足你的需求。比如,覆盖Thread类的run()方法,默认情况下,它没有用并且应该被覆盖以提供一些功能。
定制ThreadPoolExecutor类::::::::::::::::
执行者框架(Executor framework)是一种机制,允许你将线程的创建与执行分离。它是基于Executor和ExecutorService接口及其实现这两个接口的ThreadPoolExecutor类。它有一个内部的线程池和提供允许你提交两种任务给线程池执行的方法。这些任务是:
 
Runnable接口,实现没有返回结果的任务
Callable接口,实现返回结果的任务
在这两种情况下,你只有提交任务给执行者。这个执行者使用线程池中的线程或创建一个新的线程来执行这些任务。执行者同样决定任务被执行的时刻。
 
在这个指南中,你将学习如何覆盖ThreadPoolExecutor类的一些方法,计算你在执行者中执行的任务的执行时间,并且将关于执行者完成它的执行的统计信息写入到控制台。
1. package demo40;
2. import java.util.Date;
3. import java.util.List;
4. import java.util.concurrent.BlockingQueue;
5. import java.util.concurrent.ConcurrentHashMap;
6. import java.util.concurrent.ExecutionException;
7. import java.util.concurrent.Future;
8. import java.util.concurrent.ThreadPoolExecutor;
9. import java.util.concurrent.TimeUnit;
10. public class MyExecutor extends ThreadPoolExecutor {
11. private ConcurrentHashMap<String, Date> startTimes;
12. public MyExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
13. super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
14. // TODO Auto-generated constructor stub
15. startTimes = new ConcurrentHashMap<>();
16. }
17. @Override
18. public void shutdown() {
19. // TODO Auto-generated method stub
20. System.out.printf("MyExecutor: Going to shutdown.\n");
21. System.out.printf("MyExecutor: Executed tasks:%d\n",getCompletedTaskCount());
22. System.out.printf("MyExecutor: Running tasks:%d\n",getActiveCount());
23. System.out.printf("MyExecutor: Pending tasks:%d\n",getQueue().size());
24. super.shutdown();
25. }
26. @Override
27. public List<Runnable> shutdownNow() {
28. // TODO Auto-generated method stub
29. System.out.printf("MyExecutor: Going to shutdown immediately.\n");
30. System.out.printf("MyExecutor: Executed tasks:%d\n",getCompletedTaskCount());
31. System.out.printf("MyExecutor: Running tasks:%d\n",getActiveCount());
32. System.out.printf("MyExecutor: Pending tasks:%d\n",getQueue().size());
33. return super.shutdownNow();
34. }
35. @Override
36. protected void beforeExecute(Thread t, Runnable r) {
37. // TODO Auto-generated method stub
38. System.out.printf("MyExecutor: A task is beginning: %s :%s\n",t.getName(),r.hashCode());
39. startTimes.put(String.valueOf(r.hashCode()), new Date());
40. super.beforeExecute(t, r);
41. }
42. @Override
43. protected void afterExecute(Runnable r, Throwable t) {
44. // TODO Auto-generated method stub
45. Future<?> result = (Future<?>) r;
46. System.out.printf("*********************************\n");
47. System.out.printf("MyExecutor: A task is finishing.\n");
48. try {
49. System.out.printf("MyExecutor: Result: %s\n",result.get());
50. Date startDate = startTimes.remove(String.valueOf(r.hashCode()));
51. Date finishDate = new Date();
52. long diff = finishDate.getTime() - startDate.getTime();
53. System.out.printf("MyExecutor: Duration: %d\n",diff);
54. System.out.printf("*********************************\n");
55. } catch (InterruptedException e) {
56. // TODO Auto-generated catch block
57. e.printStackTrace();
58. } catch (ExecutionException e) {
59. // TODO Auto-generated catch block
60. e.printStackTrace();
61. }
62. super.afterExecute(r, t);
63. }
64. }
1. package demo40;
2. import java.util.Date;
3. import java.util.concurrent.Callable;
4. import java.util.concurrent.TimeUnit;
5. public class SleepTwoSecondsTask implements Callable<String> {
6. @Override
7. public String call() throws Exception {
8. // TODO Auto-generated method stub
9. TimeUnit.SECONDS.sleep(2);
10. return new Date().toString();
11. }
12. }
1. package demo40;
2. import java.util.ArrayList;
3. import java.util.List;
4. import java.util.concurrent.ExecutionException;
5. import java.util.concurrent.Future;
6. import java.util.concurrent.LinkedBlockingDeque;
7. import java.util.concurrent.TimeUnit;
8. public class Main {
9. public static void main(String[] args) throws Exception, ExecutionException {
10. MyExecutor myExecutor = new MyExecutor(2, 4, 1000, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<Runnable>());
11. List<Future<String>> results = new ArrayList<>();
12. for (int i = 0; i < 10; i++) {
13. SleepTwoSecondsTask task = new SleepTwoSecondsTask();
14. Future<String> result = myExecutor.submit(task);
15. results.add(result);
16. }
17. for (int i = 0; i < 5; i++) {
18. String result = results.get(i).get();
19. System.out.printf("Main: Result for Task %d :%s\n",i,result);
20. }
21. myExecutor.shutdown();
22. for (int i = 5; i < 10; i++) {
23. String result = results.get(i).get();
24. System.out.printf("Main: Result for Task %d :%s\n",i,result);
25. }
26. myExecutor.awaitTermination(1, TimeUnit.DAYS);
27. System.out.printf("Main: End of the program.\n");
28. }
29. }
在这个指南中,我们已经通过继承ThreadPoolExecutor类和覆盖它的4个方法来实现我们自己定制的执行者。我们用beforeExecute()和afterExecute()方法来计算任务的执行时间。beforeExecute()方法是在任务执行之前被执行的。在这种情况下,我们使用HashMap来存储任务的开始(执行)时间。afterExecute()方法是在任务执行之后被执行的。你可以从HashMap中获取已完成任务的startTime(开始执行时间),然后,计算实际时间和那个时间(startTime)的差异来获取任务的执行时间。你也覆盖了shutdown()和shutdownNow()方法,将关于在执行者中已执行的任务的统计信息写入到控制台:
对于已执行的任务,使用getCompletedTaskCount()方法(获取)。
对于正在运行的任务,使用getActiveCount()方法(获取)。
对于待处理任务,使用执行者存储待处理任务的阻塞队列的size()方法(获取)。SleepTwoSecondsTask类,实现Callable接口,令它的执行线程睡眠2秒。Main类,使用它向你的执行者提交10个任务和演示其他类的特性。
执行这个程序,你将看到这个程序如何显示正在运行的每个任务的时间跨度,和根据调用shutdown()方法统计执行者。
实现一个基于优先级的Executor类:::::::::::
在Java并发API的第一个版本中,你必须创建和运行应用程序中的所有线程。在Java版本5中,随着执行者框架(Executor framework)的出现,对于并发任务的执行,一个新的机制被引进。
使用执行者框架(Executor framework),你只要实现你的任务并把它们提交给执行者。这个执行者负责执行你的任务的线程的创建和执行。
在内部,一个执行者使用一个阻塞队列来存储待处理任务。以任务到达执行者的顺序来存储。一个可能的替代就是使用一个优先级列队来存储新的任务。这样,如果一个高优先级的新任务到达执行者,它将比其他已经在等待一个线程来执行它们,且低优先级的任务先执行。
在这个指南中,你将学习如何实现一个执行者,它将使用优先级队列来存储你提交执行的任务。
1. package demo41;
2. import java.util.concurrent.TimeUnit;
3. public class MyPriorityTask implements Runnable, Comparable<MyPriorityTask> {
4. private int priority;
5. private String name;
6. public MyPriorityTask(int priority, String name) {
7. super();
8. this.priority = priority;
9. this.name = name;
10. }
11. public int getPriority() {
12. return priority;
13. }
14. @Override
15. public int compareTo(MyPriorityTask o) {
16. // TODO Auto-generated method stub
17. if (this.getPriority() < o.getPriority()) {
18. return 1;
19. }else if (this.getPriority() > o.getPriority()) {
20. return -1;
21. }
22. return 0;
23. }
24. @Override
25. public void run() {
26. // TODO Auto-generated method stub
27. System.out.printf("MyPriorityTask: %s Priority :%d\n",name,priority);
28. try {
29. TimeUnit.SECONDS.sleep(2);
30. } catch (InterruptedException e) {
31. // TODO Auto-generated catch block
32. e.printStackTrace();
33. }
34. }
35. }
1. package demo41;
2. import java.util.concurrent.PriorityBlockingQueue;
3. import java.util.concurrent.ThreadPoolExecutor;
4. import java.util.concurrent.TimeUnit;
5. public class Main {
6. public static void main(String[] args) throws Exception {
7. // TODO Auto-generated method stub
8. ThreadPoolExecutor executor = new ThreadPoolExecutor(2,2,1,TimeUnit.SECONDS,new PriorityBlockingQueue<Runnable>());
9. for (int i = 0; i < 4; i++) {
10. MyPriorityTask task = new MyPriorityTask(i, "task"+i);
11. executor.execute(task);
12. }
13. TimeUnit.SECONDS.sleep(1);
14. for (int i = 4; i < 8; i++) {
15. MyPriorityTask task = new MyPriorityTask(i, "task"+i);
16. executor.execute(task);
17. }
18. executor.shutdown();
19. executor.awaitTermination(1, TimeUnit.DAYS);
20. System.out.printf("Main: End of the program.\n");
21. }
22. }
很容易将执行者转换成一个基于优先级的(执行者)。你只要传入一个参数化为Runnable接口的PriorityBlockingQueue对象作为参数。但是,使用执行者时,你应该知道存储在优先级列队中的所有对象必须实现Comparable接口。  
你已经实现了MyPriorityTask类,(作为一个任务)它实现了Runnable接口和Comparable接口,它被存储在优先级队列中。这个类有一个Priority属性,用来存储任务的优先级。如果一个任务的这个属性有更高的值,它将被更早的执行。compareTo()方法决定任务在优先级列队中的顺序。在Main类,你提交8个不同优先级的任务给执行者。你提交给执行者的第一个任务将第一个被执行。由于执行者闲置的,正在等待任务被执行,当第一个任务到达执行者时,执行者立即执行它们。你已经创建有2个执行线程的执行者,所以,前两个任务将第一个被执行。然后,剩下的任务将按它们的优先级来执行
以使用任何实现BlockingQueue接口(的队列)来配置执行者。DelayQueue是一个有趣的实现。这个类被用来存储延迟激活(delayed activation)的元素。它提供只返回活动对象的方法。你可以使用这个类来实现自己版本的ScheduledThreadPoolExecutor类。
实现ThreadFactory接口生成自定义的线程::::::::::::::::

在面向对象编程的世界中,工厂模式(factory pattern)是一个被广泛使用的设计模式。它是一个创建模式,它的目的是开发一个类,这个类的使命是创建一个或多个类的对象。然后,当我们要创建一个类的一个对象时,我们使用这个工厂而不是使用new操作。
使用这个工厂,我们集中对象的创建,获取容易改变创建对象的类的优势,或我们创建这些对象的方式,容易限制创建对象的有限资源。比如,我们只能有一个类型的N个对象,就很容易产生关于对象创建的统计数据。
Java提供ThreadFactory接口,用来实现一个Thread对象工厂。Java并发API的一些高级工具,如执行者框架(Executor framework)或Fork/Join框架(Fork/Join framework),使用线程工厂创建线程。
在Java并发API中的其他工厂模式的例子是Executors类。它提供许多方法来创建不同类型的Executor对象。  
在这个指南中,你将继承Thread类,以添加新功能,并且你将实现一个线程工厂来创建这个新类的线程。
1. package demo42;
2. import java.util.Date;
3. public class MyThread extends Thread{
4. private Date creationDate;
5. private Date startDate;
6. private Date finishDate;
7. public void setCreationDate() {
8. this.creationDate = new Date();
9. }
10. public void setStartDate() {
11. this.startDate = new Date();
12. }
13. public void setFinishDate() {
14. this.finishDate = new Date();
15. }
16. public MyThread(Runnable target,String name){
17. super(target, name);
18. setCreationDate();
19. }
20. @Override
21. public void run() {
22. // TODO Auto-generated method stub
23. setStartDate();
24. super.run();
25. setFinishDate();
26. }
27. public long getExcutionTime(){
28. return finishDate.getTime() - startDate.getTime();
29. }
30. @Override
31. public String toString() {
32. StringBuilder sb = new StringBuilder();
33. sb.append(getName());
34. sb.append(":creationDate:");
35. sb.append(creationDate);
36. sb.append(":running time:");
37. sb.append(getExcutionTime());
38. return sb.toString();
39. }
40. }
1. package demo42;
2. import java.util.concurrent.ThreadFactory;
3. public class MyThreadFactory implements ThreadFactory {
4. private int counter;
5. private String prefix;
6. public MyThreadFactory(String prefix) {
7. super();
8. this.prefix = prefix;
9. this.counter = 1;
10. }
11. @Override
12. public Thread newThread(Runnable r) {
13. MyThread myThread = new MyThread(r, prefix+"-"+counter);
14. counter++;
15. return myThread;
16. }
17. }
1. package demo42;
2. import java.util.concurrent.TimeUnit;
3. public class MyTask implements Runnable {
4. @Override
5. public void run() {
6. // TODO Auto-generated method stub
7. try {
8. TimeUnit.SECONDS.sleep(2);
9. } catch (InterruptedException e) {
10. // TODO Auto-generated catch block
11. e.printStackTrace();
12. }
13. }
14. }
1. package demo42;
2. public class Main {
3. public static void main(String[] args) throws Exception {
4. MyThreadFactory myThreadFactory = new MyThreadFactory("mythreadfactory");
5. MyTask task = new MyTask();
6. Thread thread = myThreadFactory.newThread(task);
7. thread.start();
8. thread.join();
9. System.out.printf("Main: Thread information.\n");
10. System.out.printf("%s\n",thread);
11. System.out.printf("Main: End of the example.\n");
12. }
13. }
在这个指南中,你已经通过继承Thread类来实现自定义的MyThread类。这个类有3个属性用来存储:创建日期、执行的开始日期和执行的结束日期。你已实现getExecutionTime()方法,使用开始日期和结束日期属性,返回线程已执行任务的时间。最后,你已覆盖toString()方法来产生关于线程的信息。
一旦你有自己的线程类,你已实现一个工厂来创建这个实现了ThreadFactory接口的类的对象。如果你要使用你的工厂作为一个独立的对象,这个接口的使用并不是强制的,但是如果你想要用这个工厂使用Java并发API的其他类,你必须通过实现这个接口来构建你的工厂。ThreadFactory接口只有一个方法,newThread()方法接收一个Runnable对象作为参数,并且返回一个用来执行Runnable对象的Thread对象。在你的例子中,你返回一个MyThread对象。
检查这两个类,你已实现MyTask类,这个类实现了Runnable对象。这是将在由MyThread对象管理的线程中执行的任务。一个MyTask实例令它的执行线程睡眠2秒。
在这个例子的主方法中,你已使用MyThreadFactory工厂创建一个MyThread对象,用来执行一个Task对象。执行这个程序,你将看到一条关于开始日期和线程执行的执行时间的信息。
在一个Executor对象中使用我们的ThreadFactory:::::::::::::::::::::::::::::::::::::
在前面的指南中,实现ThreadFactory接口生成自定义线程,我们引进了工厂模式和提供如何实现一个实现ThreadFactory接口的线程的工厂例子。  
执行者框架(Executor framework)是一种机制,它允许你将线程的创建与执行分离。它是基于Executor、ExecutorService接口和实现这两个接口的ThreadPoolExecutor类。它有一个内部的线程池和提供一些方法,这些方法允许你提交两种任务给线程池执行。这两种任务是:
实现Runnable接口的类,用来实现没有返回结果的任务
实现Callable接口的类,用来实现有返回结果的任务
在执行者框架(Executor framework)的内部,它提供一个ThreadFactory接口来创建线程,这是用来产生新的线程。在这个指南中,你将学习如何实现你自己的线程类,用一个工厂来创建这个类的线程,及如何在执行者中使用这个工厂,所以这个执行者将执行你的线程。
1. package demo42;
2. import java.util.concurrent.ExecutorService;
3. import java.util.concurrent.Executors;
4. import java.util.concurrent.TimeUnit;
5. public class Main {
6. public static void main(String[] args) throws Exception {
7. MyThreadFactory myThreadFactory = new MyThreadFactory("mythreadfactory");
8. ExecutorService executor = Executors.newCachedThreadPool(myThreadFactory);
9. MyTask task = new MyTask();
10. executor.submit(task);
11. executor.shutdown();
12. executor.awaitTermination(1, TimeUnit.DAYS);
13. System.out.println("Main: End of the program");
14. }
15. }
在这个例子的main()方法中,你已使用Executors类的newCachedThreadPool()方法创建一个Executor对象。你已传入之前创建的工厂对象作为参数,所以已创建的Executor对象将使用这个工厂来创建它所需的线程,并且它将执行MyThread类的线程。
自定义在计划的线程池内运行的任务:::::::::::::::::::::::::::::::::::::::::::::
计划的线程池是 Executor 框架的基本线程池的扩展,允许你定制一个计划来执行一段时间后需要被执行的任务。 它通过 ScheduledThreadPoolExecutor 类来实现,并允许运行以下这两种任务:
Delayed 任务:这种任务在一段时间后仅执行一次。
Periodic 任务:这种任务在延迟后执行,然后通常周期性运行
Delayed 任务可以执行 Callable 和 Runnable 对象,但是 periodic任务只能执行 Runnable 对象。全部任务通过计划池执行的都必须实现 RunnableScheduledFuture 接口。在这个指南,你将学习如何实现你自己的 RunnableScheduledFuture 接口来执行延迟和周期性任务。
1. package demo43;
2. import java.util.Date;
3. import java.util.concurrent.Delayed;
4. import java.util.concurrent.FutureTask;
5. import java.util.concurrent.RunnableScheduledFuture;
6. import java.util.concurrent.ScheduledThreadPoolExecutor;
7. import java.util.concurrent.TimeUnit;
8. public class MyScheduledTask<V> extends FutureTask<V> implements RunnableScheduledFuture<V> {
9. private RunnableScheduledFuture<V> task;
10. private ScheduledThreadPoolExecutor executor;
11. private long period;
12. private long startDate;
13. public MyScheduledTask(Runnable runnable,V result,RunnableScheduledFuture<V> task,ScheduledThreadPoolExecutor executor){
14. super(runnable, result);
15. this.task = task;
16. this.executor = executor;
17. }
18. @Override
19. public long getDelay(TimeUnit unit) {
20. if (!isPeriodic()) {
21. return task.getDelay(unit);
22. }else {
23. if (startDate == 0) {
24. return task.getDelay(unit);
25. }else {
26. Date now = new Date();
27. long delay = startDate - now.getTime();
28. return unit.convert(delay, TimeUnit.MILLISECONDS);
29. }
30. }
31. }
32. @Override
33. public int compareTo(Delayed o) {
34. return task.compareTo(o);
35. }
36. @Override
37. public boolean isPeriodic() {
38. // TODO Auto-generated method stub
39. return task.isPeriodic();
40. }
41. @Override
42. public void run() {
43. if (isPeriodic() && !executor.isShutdown()) {
44. Date now = new Date();
45. startDate = now.getTime() + period;
46. executor.getQueue().add(this);
47. }
48. System.out.printf("Pre-MyScheduledTask: %s\n",new Date());
49. System.out.printf("MyScheduledTask: Is Periodic:%s\n",isPeriodic());
50. super.runAndReset();
51. System.out.printf("Post-MyScheduledTask: %s\n",new Date());
52. }
53. public void setPeriod(long period) {
54. this.period = period;
55. }
56. }
1. package demo43;
2. import java.util.concurrent.RunnableScheduledFuture;
3. import java.util.concurrent.ScheduledFuture;
4. import java.util.concurrent.ScheduledThreadPoolExecutor;
5. import java.util.concurrent.TimeUnit;
6. public class MyScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor {
7. public MyScheduledThreadPoolExecutor(int corePoolSize){
8. super(corePoolSize);
9. }
10. @Override
11. protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) {
12. // TODO Auto-generated method stub
13. MyScheduledTask<V> myTask = new MyScheduledTask<V>(runnable, null, task, this);
14. return myTask;
15. }
16. @Override
17. public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
18. // TODO Auto-generated method stub
19. ScheduledFuture<?> task = super.scheduleAtFixedRate(command, initialDelay, period, unit);
20. MyScheduledTask<?> myTask = (MyScheduledTask<?>) task;
21. myTask.setPeriod(TimeUnit.MILLISECONDS.convert(period, unit));
22. return task;
23. }
24. }
1. package demo43;
2. import java.util.concurrent.TimeUnit;
3. public class Task implements Runnable {
4. @Override
5. public void run() {
6. // TODO Auto-generated method stub
7. System.out.printf("Task: Begin.\n");
8. try {
9. TimeUnit.SECONDS.sleep(2);
10. } catch (InterruptedException e) {
11. // TODO Auto-generated catch block
12. e.printStackTrace();
13. }
14. System.out.printf("Task: End.\n");
15. }
16. }
1. package demo43;
2. import java.util.Date;
3. import java.util.concurrent.TimeUnit;
4. public class Main {
5. public static void main(String[] args) throws Exception {
6. MyScheduledThreadPoolExecutor executor = new MyScheduledThreadPoolExecutor(2);
7. Task task = new Task();
8. System.out.printf("Main: %s\n",new Date());
9. //使用 schedule() 方法发送一个延迟任务给执行者。此任务在延迟一秒后运行。
10. executor.schedule(task, 1, TimeUnit.SECONDS);
11. TimeUnit.SECONDS.sleep(3);
12. task = new Task();
13. System.out.printf("Main: %s\n",new Date());
14. //25. 使用方法 scheduleAtFixedRate()发送一个周期性任务给执行者。此任务在延迟一秒后被运行,然后每3秒执行。
15. executor.scheduleAtFixedRate(task, 1, 3, TimeUnit.SECONDS);
16. TimeUnit.SECONDS.sleep(10);
17. executor.shutdown();
18. executor.awaitTermination(1, TimeUnit.DAYS);
19. System.out.printf("Main: End of the program.\n");
20. }
21. }

在这个指南,你实现了 MyScheduledTask 类实现在 ScheduledThreadPoolExecutor 执行者中执行的自定义任务。这个类扩展 FutureTask 类并实现了 RunnableScheduledFuture 接口。它实现 RunnableScheduledFuture 接口, 因为在计划的执行者中执行的全部任务都一定要实现 这个接口,并扩展了 FutureTask 类,因为这个类提供了能有效的实现在 RunnableScheduledFuture 接口声明的方法。 之前提到的全部接口和类都被参数化成任务要返回的数据类型。
 
为了在计划的执行者中使用 MyScheduledTask 任务,要重写在 MyScheduledThreadPoolExecutor 类的 decorateTask() 方法。这个类扩展 ScheduledThreadPoolExecutor 执行者和它的方法提供一个把 ScheduledThreadPoolExecutor 执行者默认的计划任务转换成 MyScheduledTask 任务来实现的机制。所以,当你实现你的版本的计划任务时,你必须实现你的版本的计划的执行者。
 
decorateTask() 方法只是简单的创建了新的带有参数的 MyScheduledTask 对象:将要在任务中执行的 Runnable 对象; 将被任务返回结果对象,在这个例子,任务将不会返回结果,所以你要使用null值;原来执行 Runnable 对象的任务,新的对象将在池中代替这个任务;和
将执行任务的执行者,在这个例子,你使用 this 关键词指向创建这个任务的执行者。
 
The MyScheduledTask 类可以执行延迟和周期性任务。你已经实现了有全部必须的算法可以执行这2种任务的方法。他们是 getDelay() 和 run() 方法。
 
The getDelay() 方法被计划的执行者调用来确认它是否需要运行任务。此方法对延迟任务和周期任务的响应是不同的。在之前提到的, MyScheduledClass 类的构造函数接收 原先的将要执行 Runnable 对象的 ScheduledRunnableFuture 对象, 并储存它作为类的属性来获取它的方法和它的数据。当我们要运行延迟任务时,getDelay() 方法返回原先任务的延迟,但是在周期任务的例子中,getDelay() 方法返回 startDate 属性值与当前时间的相差值。
run() 方法是用来执行任务的。周期性任务的一个特别之处是你必须把下一次任务的执行作为一个新的任务放入到执行者的queue中,如果你要再次运行任务的话。所以,如果你执行周期性任务,你确定 startDate 属性值通过把当前时间和任务的执行周期相加,然后把任务储存在执行者的queue中。startDate 属性储存下一次任务将开始运行的时间。然后,使用 FutureTask 类提供的 runAndReset() 方法来运行任务。 在这个例子的延迟任务由于他们仅仅执行一次,就不用把他们放入执行者的queue中了。
你必须要注意如果执行者已经关闭。在这个例子,你不不需要再次把周期性任务储存进执行者的queue。
 
最后,你重写了在 MyScheduledThreadPoolExecutor 类的 scheduleAtFixedRate() 方法。我们之前提到的,对于周期任务,你要使用任务的周期来确定 startDate 属性值,但是你还没有初始这个周期呢。你必须重写此方法接收周期作为参数,然后传递给 MyScheduledTask 类这样它才能使用。
 
有了 Task 类例子总是完成了,它实现 Runnable 接口,也是在计划的执行者中运行的任务。这个例子的主类创建了 MyScheduledThreadPoolExecutor 执行者,然后给他们发送了以下2个任务:
 
一个延迟任务,在当前时间过一秒后运行
一个周期任务,在当前时间过一秒后运行,接着每隔3秒运行
实现ThreadFactory接口生成自定义的线程给Fork/Join框架:::::::::::::::::::::::::::::::::::::::::::::::::::::
Fork/Join框架是Java7中最有趣的特征之一。它是Executor和ExecutorService接口的一个实现,允许你执行Callable和Runnable任务而不用管理这些执行线程。
 
这个执行者面向执行能被拆分成更小部分的任务。主要组件如下:
 
一个特殊任务,实现ForkJoinTask类
两种操作,将任务划分成子任务的fork操作和等待这些子任务结束的join操作
一个算法,优化池中线程的使用的work-stealing算法。当一个任务正在等待它的子任务(结束)时,它的执行线程将执行其他任务(等待执行的任务)。
ForkJoinPool类是Fork/Join的主要类。在它的内部实现,有如下两种元素:
 
一个存储等待执行任务的列队。
一个执行任务的线程池
在这个指南中,你将学习如何实现一个在ForkJoinPool类中使用的自定义的工作者线程,及如何使用一个工厂来使用它。
1. package demo44;
2. import java.util.concurrent.ForkJoinPool;
3. import java.util.concurrent.ForkJoinWorkerThread;
4. public class MyWorkerThread extends ForkJoinWorkerThread {
5. private static ThreadLocal<Integer> taskCounter = new ThreadLocal<>();
6. protected MyWorkerThread(ForkJoinPool pool) {
7. super(pool);
8. }
9. @Override
10. protected void onStart() {
11. // TODO Auto-generated method stub
12. super.onStart();
13. System.out.printf("MyWorkerThread %d: Initializing task counter.\n",getId());
14. taskCounter.set(0);
15. }
16. @Override
17. protected void onTermination(Throwable exception) {
18. // TODO Auto-generated method stub
19. System.out.printf("MyWorkerThread %d:%d\n",getId(),taskCounter.get());
20. super.onTermination(exception);
21. }
22. public void addTask(){
23. int counter = taskCounter.get().intValue();
24. counter ++;
25. taskCounter.set(counter);
26. }
27. public ForkJoinWorkerThread newThread(ForkJoinPool pool){
28. return new MyWorkerThread(pool);
29. }
30. }

Fork/Join框架使用的线程叫工作者线程。Java包含继承Thread类的ForkJoinWorkerThread类和使用Fork/Join框架实现工作者线程。
 
在这个指南中,你已实现了继承ForkJoinWorkerThread类的MyWorkerThread类,并重写这个类的两个方法。你的目标是实现每个工作者线程的任务计数器,以至于你可以知道每个工作者线程执行多少个任务。你已经通过一个ThreadLocal属性实现计数器。这样,每个线程都拥有它自己的计数器,对于来你说是透明的。
 
你已重写ForkJoinWorkerThread类的onStart()方法来实现任务的计数器。当工作者线程开始它的执行时,这个方法将被调用。你也重写了onTermination()方法,将任务计数器的值写入到控制台。当工作者线程结束它的执行时,这个方法将被调用。你也在MyWorkerThread类中实现addTask()方法,用来增加每个线程的任务计数器。
 
对于ForkJoinPool类,与Java并发API中的所有执行者一样,使用工厂来创建它。所以,如果你想在ForkJoinPool类中使用MyWorkerThread线程,你必须实现自己的线程工厂。对于Fork/Join框架,这个工厂必须实现ForkJoinPool.ForkJoinWorkerThreadFactory类。为此,你已实现MyWorkerThreadFactory类。这个类只有一个用来创建一个新的MyWorkerThread对象的方法。
 
最后,你只要使用已创建的工厂来初始化ForkJoinPool类。你已在Main类中通过使用ForkJoinPool的构造器实现了。
自定义在 Fork/Join 框架中运行的任务::::::::::::::::::::
执行者框架分开了任务的创建和运行。这样,你只要实现 Runnable 对象来使用 Executor 对象。你可以发送 Runnable 任务给执行者,然后它会创建,管理,并终结必要的线程来执行这些任务。
 
Java 7 在 Fork/Join 框架中提供了特殊的执行者。这个框架是设计用来解决那些可以使用 divide 和 conquer 技术分成更小点的任务的问题。在一个任务内,你要检查你要解决的问题的大小,如果它比设定的大小还大,你就把问题分成2个或多个任务,再使用框架来执行这些任务。
 
如果问题的大小比设定的大小要小,你可以在任务中直接解决问题,可选择返回结果。Fork/Join 框架实现 work-stealing 算法来提高这类问题的整体表现。
 
Fork/Join 框架的主要类是 ForkJoinPool 类。它内部有以下2个元素:
 
一个等待执行的任务queue
一个执行任务的线程池
默认情况,被 ForkJoinPool类执行的任务是 ForkJoinTask 类的对象。你也可以发送 Runnable 和 Callable 对象给 ForkJoinPool 类,但是他们就不能获得所以 Fork/Join 框架的好处。通常情况,你将发送ForkJoinTask 类的这2个子类中的一个给 ForkJoinPool 对象:
 
RecursiveAction: 如果你的任务没有返回结果
RecursiveTask: 如果你的任务返回结果
在这个指南,你将学习如何为 Fork/Join 框架实现你自己的任务,实现一个任务扩展ForkJoinTask类。你将要实现的任务是计量运行时间并写入操控台,这样你可以控制它的进展(evolution)。你也可以实现你自己的 Fork/Join 任务来写日志信息,为了获得在这个任务中使用的资源,或者来 post-process 任务的结果。
1. package demo44;
2. import java.util.Date;
3. import java.util.concurrent.ForkJoinTask;
4. public abstract class MyWorkTask extends ForkJoinTask<Void> {
5. private String name;
6. public MyWorkTask(String name) {
7. super();
8. this.name = name;
9. }
10. @Override
11. public Void getRawResult() {
12. // TODO Auto-generated method stub
13. return null;
14. }
15. @Override
16. protected void setRawResult(Void value) {
17. // TODO Auto-generated method stub
18. }
19. @Override
20. protected boolean exec() {
21. // TODO Auto-generated method stub
22. Date startDate = new Date();
23. compute();
24. Date finishDate = new Date();
25. long diff = finishDate.getTime() - startDate.getTime();
26. System.out.printf("MyWorkerTask: %s : %d Milliseconds to complete.\n",name,diff);
27. return true;
28. }
29. public String getName() {
30. return name;
31. }
32. protected abstract void compute();
33. }
1. package demo44;
2. public class Task extends MyWorkTask {
3. private int array[];
4. private int start,end;
5. public Task(String name, int[] array, int start, int end) {
6. super(name);
7. this.array = array;
8. this.start = start;
9. this.end = end;
10. }
11. @Override
12. protected void compute() {
13. // TODO Auto-generated method stub
14. if (end - start > 100) {
15. int mid = (end + start) / 2;
16. Task task1 = new Task(this.getName()+"1", array, start, mid);
17. Task task2 = new Task(this.getName()+"2", array, mid, end);
18. invokeAll(task1,task2);
19. }else {
20. for (int i = start; i < end; i++) {
21. array[i]++;
22. }
23. try {
24. Thread.sleep(50);
25. } catch (InterruptedException e) {
26. // TODO Auto-generated catch block
27. e.printStackTrace();
28. }
29. }
30. }
31. }
1. package demo44;
2. import java.util.concurrent.ForkJoinPool;
3. public class Main {
4. public static void main(String[] args) {
5. int array[] = new int[10000];
6. ForkJoinPool pool = new ForkJoinPool();
7. Task task = new Task("task", array, 0, 10000);
8. pool.invoke(task);
9. pool.shutdown();
10. System.out.printf("Main: End of the program.\n");
11. }
12. }
在这个指南,你实现了扩展 ForkJoinTask 类的 MyWorkerTask 类。这是你的基本类,它可以在 ForkJoinPool 执行者中执行,并且可以获得这个执行者的所以好处,如 work-stealing 算法。这个类等同于 RecursiveAction 和 RecursiveTask 类。
 
当你扩展 ForkJoinTask 类,你必须实现以下3个方法:
 
setRawResult(): 此方法是用来设立任务的结果。由于你的任务不返回任何结果,所以留白即可。
getRawResult(): 此方法是用来返回任务的结果。由于你的任务不返回任何结果,此方法会返回null值。
exec(): 此方法实现了任务的算法。在这个例子,你把算法委托给抽象方法 compute() (如 RecursiveAction 类和 RecursiveTask 类),且在exec()方法你计算了方法的运行时间,并写入到操控台。
最后,在例子的主类,你创建了一个有10,000个元素的array,一个 ForkJoinPool 执行者,和一个处理整个array的 Task 对象。运行程序,你可以发现不同的任务运行后都在操控台写入他们的运行时间。
实现一个自定义的Lock类:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
锁是Java并发API提供的基本同步机制之一。它允许程序员保护代码的临界区,所以,在某个时刻只有一个线程能执行这个代码块。它提供以下两种操作:
lock():当你想要访问一个临界区时,调用这个方法。如果有其他线程正在运行这个临界区,其他线程将阻塞,直到它们被这个锁唤醒,从而获取这个临界区的访问。
unlock():你在临界区的尾部调用这个方法,允许其他线程访问这个临界区。
在Java并发API中,锁是在Lock接口及其一些实现类中声明的,比如ReentrantLock类。
在这个指南中,你将学习如何实现你自己的Lock对象,它将实现一个实现了Lock接口并可用来保护临界区的类。
1. package demo45;
2. import java.util.concurrent.atomic.AtomicInteger;
3. import java.util.concurrent.locks.AbstractQueuedSynchronizer;
4. public class MyAbstractQueuedSynchronizer extends AbstractQueuedSynchronizer{
5. private AtomicInteger state;
6. public MyAbstractQueuedSynchronizer(){
7. state = new AtomicInteger(0);
8. }
9. @Override
10. protected boolean tryAcquire(int arg) {
11. // TODO Auto-generated method stub
12. return state.compareAndSet(0, 1);
13. }
14. @Override
15. protected boolean tryRelease(int arg) {
16. // TODO Auto-generated method stub
17. return state.compareAndSet(1, 0);
18. }
19. }
1. package demo45;
2. import java.util.concurrent.TimeUnit;
3. import java.util.concurrent.locks.AbstractQueuedSynchronizer;
4. import java.util.concurrent.locks.Condition;
5. import java.util.concurrent.locks.Lock;
6. public class MyLock implements Lock {
7. private AbstractQueuedSynchronizer sync;
8. public MyLock() {
9. super();
10. this.sync = new MyAbstractQueuedSynchronizer();
11. }
12. @Override
13. public void lock() {
14. // TODO Auto-generated method stub
15. sync.acquire(1);
16. }
17. @Override
18. public void lockInterruptibly() throws InterruptedException {
19. // TODO Auto-generated method stub
20. sync.acquireInterruptibly(1);
21. }
22. @Override
23. public boolean tryLock() {
24. // TODO Auto-generated method stub
25. try {
26. return sync.tryAcquireNanos(1, 1000);
27. } catch (InterruptedException e) {
28. // TODO Auto-generated catch block
29. e.printStackTrace();
30. }
31. return false;
32. }
33. @Override
34. public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
35. // TODO Auto-generated method stub
36. return sync.tryAcquireNanos(1, TimeUnit.NANOSECONDS.convert(time, unit));
37. }
38. @Override
39. public void unlock() {
40. // TODO Auto-generated method stub
41. sync.release(1);
42. }
43. @Override
44. public Condition newCondition() {
45. // TODO Auto-generated method stub
46. return sync.new ConditionObject();
47. }
48. }
1. package demo45;
2. import java.util.concurrent.TimeUnit;
3. public class Task implements Runnable {
4. private MyLock lock;
5. private String name;
6. public Task(MyLock lock, String name) {
7. super();
8. this.lock = lock;
9. this.name = name;
10. }
11. @Override
12. public void run() {
13. // TODO Auto-generated method stub
14. lock.lock();
15. System.out.printf("Task: %s: Take the lock\n",name);
16. try {
17. TimeUnit.SECONDS.sleep(2);
18. System.out.printf("Task: %s: Free the lock\n",name);
19. } catch (InterruptedException e) {
20. // TODO Auto-generated catch block
21. e.printStackTrace();
22. }finally{
23. lock.unlock();
24. }
25. }
26. }
1. package demo45;
2. import java.util.concurrent.TimeUnit;
3. public class Main {
4. public static void main(String[] args) {
5. MyLock lock = new MyLock();
6. for (int i = 0; i < 10; i++) {
7. Task task = new Task(lock, "task"+i);
8. Thread thread = new Thread(task);
9. thread.start();
10. }
11. boolean value = false;
12. do {
13. try {
14. value = lock.tryLock(1, TimeUnit.SECONDS);
15. if (!value) {
16. System.out.println("Main: Trying to get the Lock");
17. }
18. } catch (Exception e) {
19. // TODO: handle exception
20. value = false;
21. }
22. } while (!value);
23. System.out.printf("Main: Got the lock\n");
24. lock.unlock();
25. System.out.printf("Main: End of the program\n");
26. }
27. }
Java并发API提供一个类,可以用来实现拥有锁和信号量特征的同步机制。它就是AbstractQueuedSynchronizer,正如其名,它是一个抽象类。它提供控制临界区的访问和管理正在阻塞等待访问临界区的线程队列的操作。这些操作是基于以下两个抽象方法:
tryAcquire():尝试访问临界区时,调用这个方法。如果线程调用这个方法可以访问临界区,那么这个方法返回true,否则,返回false。
tryRelease():尝试翻译临界区的访问,调用这个方法。如果线程调用这个方法可以释放临界区的访问,那么这个方法返回true,否则,返回false.
在这些方法中,你已实现可用来控制临界区访问的机制。在你的例子中,你已实现继承AbstractQueuedSyncrhonizer类的MyQueuedSynchonizer类,并使用AtomicInteger变量实现抽象方法来控制临界区的访问。如果锁是自由的,这个变量的值为0,表明线程可以访问这个临界区。如果锁是阻塞的,这个变量的值为1,表明线程不能访问这个临界区。
你已使用AtomicInteger类提供的compareAndSet()方法,尝试将你指定的值作为第一个参数改变成你指定的值作为第二个参数。实现tryAcquire()方法,你尝试将原子变量的值从0变成1。同样地,你实现tryRelease()方法,尝试将原子变量的值从1变成0。
你必须实现这个类,因为AbstractQueuedSynchronizer类的其他实现(比如,所使用的ReentrantLock类)是作为私有的内部类使用来实现的,所以你不能访问它。
然后,你已实现MyLock类。这个类实现Lock接口,有一个MyQueuedSynchronizer对象属性。你已使用MyQueuedSynchronizer对象的方法,来实现Lock接口的所有方法。
最后,你实现了Task类,它实现了Runnable接口,并使用一个MyLock对象来控制临界区的访问。这个临界区令线程睡眠2秒。主类创建一个MyLock对象,并运行10个Task对象来共享这把锁。主类也使用tryLock()方法来尝试获取锁的访问。
当你执行这个例子,你可以看到只有一个线程可以访问这个临界区,并且当这个线程结束,其他线程可以继续访问这个临界区。
你可以使用你自己的锁来写入关于它的使用的日志信息,控制锁定时间,或实现先进的同步机制来控制。比如,只能在特定的时间内,才能对资源访问。
AbstractQueuedSynchronizer类提供两个方法可以用来控制锁的状态,它们就是getState()和setState()方法。这两个方法,接收和返回一个整数值作为锁的状态。你可以使用这两个方法而不是AtomicInteger属性来存储锁的状态。
Java并发API提供其他类来实现同步机制。它就是AbstractQueuedLongSynchronizer类,它与AbstractQueuedSynchronizer一样,除了使用一个long类型属性来存储线程的状态。
实现一个基于优先级的传输队列::::::::::
Java 7 API 提供几种与并发应用相关的数据类型。从这里面,我们想来重点介绍以下2种数据类型:
LinkedTransferQueue:这个数据类型支持那些有生产者和消费者结构的程序。 在那些应用,你有一个或者多个数据生产者,一个或多个数据消费者和一个被生产者和消费者共享的数据类型。生产者把数据放入数据结构内,然后消费者从数据结构内提取数据。如果数据结构为空,消费者会被阻塞直到有数据可以消费。如果数据结构满了,生产者就会被阻塞直到有空位来放数据。
PriorityBlockingQueue:在这个数据结构,元素是按照顺序储存的。元素们必须实现 带有 compareTo() 方法的 Comparable 接口。当你在结构中插入数据时,它会与数据元素对比直到找到它的位置。
LinkedTransferQueue 的元素是按照抵达顺序储存的,所以越早到的越先被消耗。你有可能需要开发 producer/ consumer 程序,它的消耗顺序是由优先级决定的而不是抵达时间。在这个指南,你将学习如何实现在 producer/ consumer 问题中使用的数据结构,这些元素将被按照他们的优先级排序,级别高的会先被消耗。
实现自定义的原子对象:::::::::
Java版本5中引入原子变量,并提供对单个变量的原子操作。当一个线程在原子变量上执行操作时,这个类的实现包含一种机制用来检查这个操作在一个步骤内完成。基本上,这个操作是先获取变量的值,然后在本地变量中改变这个值,最后尝试将旧值变成这个新值。如果旧值仍然是相同的,它将改变成新值,否则,这个方法重新开始这个操作。
在这个指南中,你将学习如何继承一个原子对象和如何实现遵从原子对象机制的两个操作,来保证所有的操作在一个步骤内完成。
1. package demo28;
2. import java.util.concurrent.atomic.AtomicInteger;
3. public class ParkingCounter extends AtomicInteger{
4. private int maxNumber;
5. public ParkingCounter(int maxNumber) {
6. set(0);
7. this.maxNumber = maxNumber;
8. }
9. public boolean carIn(){
10. for (; ;) {
11. int value = get();
12. if (value == maxNumber) {
13. System.out.println("parkingCounter:the parking lot is full");
14. return false;
15. }else {
16. int newValue = value + 1;
17. boolean changed = compareAndSet(value, newValue);
18. if (changed) {
19. System.out.println("parkingcounter: a car has entered.\n");
20. return true;
21. }
22. }
23. }
24. }
25. public boolean carOut(){
26. for (; ;) {
27. int value = get();
28. if (value == 0) {
29. System.out.println("parkingCounter:the parking lot is empty");
30. return false;
31. }else {
32. int newValue = value - 1;
33. boolean changed = compareAndSet(value, newValue);
34. if (changed) {
35. System.out.println("parkingcounter: a car has outed.\n");
36. return true;
37. }
38. }
39. }
40. }
41. //当一个线程在原子变量上执行操作时,这个类的实现包含一种机制用来检查这个操作在一个步骤内完成。1.先获取变量的值,然后在本地变量中改变这个值。
42. //最后尝试将旧值变成新值,如果旧值是相同的,它将改变成新值,否则,这个方法重新开始此操作。CAS的实现原理。
43. }
1. package demo28;
2. public class Seosor1 implements Runnable {
3. private ParkingCounter counter;
4. public Seosor1(ParkingCounter counter) {
5. super();
6. this.counter = counter;
7. }
8. @Override
9. public void run() {
10. // TODO Auto-generated method stub
11. counter.carIn();
12. counter.carIn();
13. counter.carIn();
14. counter.carIn();
15. counter.carOut();
16. counter.carOut();
17. counter.carIn();
18. counter.carIn();
19. }
20. }
1. package demo28;
2. public class Seosor2 implements Runnable {
3. private ParkingCounter counter;
4. public Seosor2(ParkingCounter counter) {
5. super();
6. this.counter = counter;
7. }
8. @Override
9. public void run() {
10. // TODO Auto-generated method stub
11. counter.carOut();
12. counter.carOut();
13. counter.carOut();
14. counter.carOut();
15. counter.carOut();
16. counter.carIn();
17. counter.carIn();
18. counter.carIn();
19. counter.carIn();
20. }
21. }
1. package demo28;
2. public class Main {
3. public static void main(String[] args) throws Exception{
4. ParkingCounter parkingCounter = new ParkingCounter(5);
5. Seosor1 sensor1 = new Seosor1(parkingCounter);
6. Seosor2 sensor2 = new Seosor2(parkingCounter);
7. Thread thread1 = new Thread(sensor1);
8. Thread thread2 = new Thread(sensor2);
9. thread1.start();
10. thread2.start();
11. thread1.join();
12. thread2.join();
13. System.out.printf("Main: Number of cars: %d\n",parkingCounter.get());
14. System.out.printf("Main: End of the program.\n");
15. }
16. }

继承AtomicInteger类的ParkingCounter类有两个原子操作,carIn()和carOunt()。这个例子模拟一个系统来控制停车场内的汽车数。这个停车场可容纳的汽车数用maxNumber属性表示。
carIn()操作将实际汽车数与停车场(可容纳的汽车数)的最大值进行比较。如果它们相等,这辆汽车不能进行停车场并返回false值。否则,它使用以下的原子操作结构:
用一个本地变量获取原子对象的值。
用一个不同的变量来存储新值。
使用compareAndSet()方法尝试将旧值替换成新值。如果这个方法返回true,作为参数传入的旧值是这个变量的值,因此,它使值变化。随着carIn()方法返回true值,这个操作将以原子方式完成。如果compareAndSet()方法返回false值,作为参数传入的旧值不是这个变量的值(其他线程已修改它),所以这个操作不能以原子方式完成。这个操作将重新开始,直到它可以以原子方式完成。
carOut()方法与carIn()方法类似。你已实现两个Runnable对象,使用carIn()和carOut()来模拟停车的活动。当你执行这个程序,你可以看出停车场没有克服汽车在停车场的最大值。








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值