|
A Runnable encapsulates a task that runs asynchronously; you can think of it as an asynchronous method with no parameters and no return value. A Callable is similar to a Runnable, but it returns a value. The Callable interface is a parameterized type, with a single method call. public interface Callable<V> { V call() throws Exception; } The type parameter is the type of the returned value. For example, a Callable<Integer> represents an asynchronous computation that eventually returns an Integer object. A Future holds the result of an asynchronous computation. You use a Future object so that you can start a computation, give the result to someone, and forget about it. The owner of the Future object can obtain the result when it is ready. The Future interface has the following methods: public interface Future<V> { V get() throws . . .; V get(long timeout, TimeUnit unit) throws . . .; void cancel(boolean mayInterrupt); boolean isCancelled(); boolean isDone(); } A call to the first get method blocks until the computation is finished. The second method throws a TimeoutException if the call timed out before the computation finished. If the thread running the computation is interrupted, both methods throw an InterruptedException. If the computation has already finished, then get returns immediately. The isDone method returns false if the computation is still in progress, TRue if it is finished. You can cancel the computation with the cancel method. If the computation has not yet started, it is canceled and will never start. If the computation is currently in progress, then it is interrupted if the mayInterrupt parameter is true. The FutureTask wrapper is a convenient mechanism for turning a Callable into both a Future and a Runnableit implements both interfaces. For example, Callable<Integer> myComputation = . . .; FutureTask<Integer> task = new FutureTask<Integer>(myComputation); Thread t = new Thread(task); // it's a Runnable t.start(); . . . Integer result = task.get(); // it's a Future The program in Example 1-7 puts these concepts to work. This program is similar to the preceding example that found files containing a given keyword. However, now we will merely count the number of matching files. Thus, we have a long-running task that yields an integer valuean example of a Callable<Integer>. class MatchCounter implements Callable<Integer> { public MatchCounter(File directory, String keyword) { . . . } public Integer call() { . . . } // returns the number of matching files } Then we construct a FutureTask object from the MatchCounter and use it to start a thread. FutureTask<Integer> task = new FutureTask<Integer>(counter); Thread t = new Thread(task); t.start(); Finally, we print the result. System.out.println(task.get() + " matching files."); Of course, the call to get blocks until the result is actually available. Inside the call method, we use the same mechanism recursively. For each subdirectory, we produce a new MatchCounter and launch a thread for it. We also stash the FutureTask objects away in an ArrayList<Future<Integer>>. At the end, we add up all results: for (Future<Integer> result : results) count += result.get(); Each call to get blocks until the result is available. Of course, the threads run in parallel, so there is a good chance that the results will all be available at about the same time. Example 1-7. FutureTest.java[View full width] 1. import java.io.*; 2. import java.util.*; 3. import java.util.concurrent.*; 4. 5. public class FutureTest 6. { 7. public static void main(String[] args) 8. { 9. Scanner in = new Scanner(System.in); 10. System.out.print("Enter base directory (e.g. /usr/local/jdk5.0/src): "); 11. String directory = in.nextLine(); 12. System.out.print("Enter keyword (e.g. volatile): "); 13. String keyword = in.nextLine(); 14. 15. MatchCounter counter = new MatchCounter(new File(directory), keyword); 16. FutureTask<Integer> task = new FutureTask<Integer>(counter); 17. Thread t = new Thread(task); 18. t.start(); 19. try 20. { 21. System.out.println(task.get() + " matching files."); 22. } 23. catch (ExecutionException e) 24. { 25. e.printStackTrace(); 26. } 27. catch (InterruptedException e) {} 28. } 29. } 30. 31. /** 32. This task counts the files in a directory and its subdirectories that contain a given keyword. 33. */ 34. class MatchCounter implements Callable<Integer> 35. { 36. /** 37. Constructs a MatchCounter. 38. @param directory the directory in which to start the search 39. @param keyword the keyword to look for 40. */ 41. public MatchCounter(File directory, String keyword) 42. { 43. this.directory = directory; 44. this.keyword = keyword; 45. } 46. 47. public Integer call() 48. { 49. count = 0; 50. try 51. { 52. File[] files = directory.listFiles(); 53. ArrayList<Future<Integer>> results = new ArrayList<Future<Integer>>(); 54. 55. for (File file : files) 56. if (file.isDirectory()) 57. { 58. MatchCounter counter = new MatchCounter(file, keyword); 59. FutureTask<Integer> task = new FutureTask<Integer>(counter); 60. results.add(task); 61. Thread t = new Thread(task); 62. t.start(); 63. } 64. else 65. { 66. if (search(file)) count++; 67. } 68. 69. for (Future<Integer> result : results) 70. try 71. { 72. count += result.get(); 73. } 74. catch (ExecutionException e) 75. { 76. e.printStackTrace(); 77. } 78. } 79. catch (InterruptedException e) {} 80. return count; 81. } 82. 83. /** 84. Searches a file for a given keyword. 85. @param file the file to search 86. @return true if the keyword is contained in the file 87. */ 88. public boolean search(File file) 89. { 90. try 91. { 92. Scanner in = new Scanner(new FileInputStream(file)); 93. boolean found = false; 94. while (!found && in.hasNextLine()) 95. { 96. String line = in.nextLine(); 97. if (line.contains(keyword)) found = true; 98. } 99. in.close(); 100. return found; 101. } 102. catch (IOException e) 103. { 104. return false; 105. } 106. } 107. 108. private File directory; 109. private String keyword; 110. private int count; 111. } java.util.concurrent.Callable<V> 5.0
java.util.concurrent.Future<V> 5.0
java.util.concurrent.FutureTask<V> 5.0
|
|