Spec-Zone .ru
спецификации, руководства, описания, API
|
Thread
объект к Executor.execute
? Такой вызов имел бы смысл? Почему или почему нет? Ответ: Thread
реализации Runnable
интерфейс, таким образом, можно передать экземпляр Thread
к Executor.execute
. Однако не имеет смысла использовать Thread
объекты этот путь. Если от объекта непосредственно инстанцируют Thread
, run
метод ничего не делает. Можно определить подкласс Thread
с полезным run
метод — но такой class реализовал бы опции, которые не будет использовать исполнитель.
BadThreads.java
: public class BadThreads { static String message; private static class CorrectorThread extends Thread { public void run() { try { sleep(1000); } catch (InterruptedException e) {} // Key statement 1: message = "Mares do eat oats."; } } public static void main(String args[]) throws InterruptedException { (new CorrectorThread()).start(); message = "Mares do not eat oats."; Thread.sleep(2000); // Key statement 2: System.out.println(message); } }
Приложение должно распечатать "Кобыл, действительно едят овес." Это, как гарантируют, всегда сделает это? В противном случае, почему нет? Был бы это помогать изменить параметры двух вызовов Sleep
? Как был бы Вы гарантировать что все изменения message
будет видимо к основному потоку?
Решение: программа будет почти всегда распечатывать "Кобыл, действительно едят овес." Однако, этот результат не гарантируется, потому что есть, не происходит - перед отношением между "Ключевым оператором 1" и "Ключом statment 2". Это - истина, даже если "Ключевой оператор 1" фактически выполняется, перед "Ключевой оператор 2" — помнит, происхождение - прежде, чем отношение будет о видимости, не последовательности.
Есть два способа, которыми можно гарантировать что все изменения message
будет видимо к основному потоку:
CorrectorThread
экземпляр. Затем вызовите join
на том экземпляре прежде, чем обратиться к message
message
в объекте с синхронизируемыми методами. Никогда не ссылайтесь message
кроме через те методы.Оба из этих методов устанавливают необходимое, происходит - перед отношением, производя изменения в message
видимый.
Третий метод должен просто объявить message
как volatile
. Это гарантирует что любая запись message
(как в "Ключевом операторе 1"), будет иметь происхождение - перед отношением с любыми последующими чтениями message
(как в "Ключевом операторе 2"). Но это не гарантирует, что "Ключевой оператор 1" буквально произойдет перед "Ключевым оператором 2". Они, вероятно, произойдут в последовательности, но из-за планирования uncertainities и неизвестной гранулярности sleep
, это не гарантируется.
Изменение параметров двух sleep
вызовы не помогают также, так как это не делает ничего, чтобы гарантировать происхождение - перед отношением.
Drop
class. Решение: java.util.concurrent.BlockingQueue
интерфейс определяет a get
метод, который блокирует, если очередь пуста, и a put
методы, который блокирует, если очередь полна. Они - эффективно те же самые операции, определенные Drop
— за исключением того, что Drop
не очередь! Однако, есть другой способ смотреть на Отбрасывание: это - очередь с емкостью нуля. С тех пор нет никакой комнаты в очереди ни для каких элементов, каждого get
блоки до соответствия take
и каждый take
блоки до соответствия get
. Есть реализация BlockingQueue
с точно этим поведением: java.util.concurrent.SynchronousQueue
BlockingQueue
почти понижение замены для Drop
. Основная проблема в
это с Producer
BlockingQueue
, put
и get
бросок методов InterruptedException
. Это означает что существующее try
должен быть перемещен уровень вверх:
import java.util.Random; import java.util.concurrent.BlockingQueue; public class Producer implements Runnable { private BlockingQueue<String> drop; public Producer(BlockingQueue<String> drop) { this.drop = drop; } public void run() { String importantInfo[] = { "Mares eat oats", "Does eat oats", "Little lambs eat ivy", "A kid will eat ivy too" }; Random random = new Random(); try { for (int i = 0; i < importantInfo.length; i++) { drop.put(importantInfo[i]); Thread.sleep(random.nextInt(5000)); } drop.put("DONE"); } catch (InterruptedException e) {} } }
Consumer
: import java.util.Random; import java.util.concurrent.BlockingQueue; public class Consumer implements Runnable { private BlockingQueue<String> drop; public Consumer(BlockingQueue<String> drop) { this.drop = drop; } public void run() { Random random = new Random(); try { for (String message = drop.take(); ! message.equals("DONE"); message = drop.take()) { System.out.format("MESSAGE RECEIVED: %s%n", message); Thread.sleep(random.nextInt(5000)); } } catch (InterruptedException e) {} } }
ProducerConsumerExample
, мы просто изменяем объявление для drop
объект: import java.util.concurrent.BlockingQueue; import java.util.concurrent.SynchronousQueue; public class ProducerConsumerExample { public static void main(String[] args) { BlockingQueue<String> drop = new SynchronousQueue<String> (); (new Thread(new Producer(drop))).start(); (new Thread(new Consumer(drop))).start(); } }