Spec-Zone .ru
спецификации, руководства, описания, API
Домашняя страница > Существенные Классы > Параллелизм

Ответы на Вопросы и Упражнения: Параллелизм

Вопросы

  1. Вопрос: Можете Вы передавать a Thread объект к Executor.execute? Такой вызов имел бы смысл? Почему или почему нет?

    Ответ: Thread реализации Runnable интерфейс, таким образом, можно передать экземпляр Thread к Executor.execute. Однако не имеет смысла использовать Thread объекты этот путь. Если от объекта непосредственно инстанцируют Thread, run метод ничего не делает. Можно определить подкласс Thread с полезным run метод — но такой class реализовал бы опции, которые не будет использовать исполнитель.

Упражнения

  1. Осуществление: Скомпилируйте и работайте 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 вызовы не помогают также, так как это не делает ничего, чтобы гарантировать происхождение - перед отношением.

  2. Осуществление: Измените пример производителя-потребителя в Защищенных Блоках, чтобы пользоваться стандартной библиотекой class вместо 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();
        }
    }
    
«ПредыдущийTOC

Проблемы с примерами? Попытайтесь Компилировать и Выполнить Примеры: FAQ.
Жалобы? Поздравление? Предложения? Дайте нам свою обратную связь.

Предыдущая страница: Вопросы и Упражнения: Параллелизм