Re[4]: Brian Goetz и др. Java Concurrency in Practice
От: mik1  
Дата: 09.08.06 12:46
Оценка:
Здравствуйте, bolshik, Вы писали:

B>Здравствуйте, mik1, Вы писали:


M>>...Из простых вещей, там обсуждавшихся: вы знаете, что в многопоточном коде при использовании несколькими потоками одного экземпляра следующего класса (пример не из книги, но суть оттуда) после вызова метода setter одним потоком, а метода test — другим, test может вернуть true? (речь шла о возможности переупорядочивания операций)

B>Видимо речь идет все же о переключении между потоками.

Посмотрите внимательно на то, как поля инициализированы в конструкторе, и на то, в каком порядке их устанавливает setter. Речь идет именно о переупорядочивании операций.

M>>...однако, если убрать такие требования, то класс является полностью потокобезопасным, что уже не мало.

B>Это, конечно, хорошо, но не так уж и много Мне не нравится сама идея с тем, что вычисления производятся в клиентских потоках, а не в worker thread.

Ну в данном классе — да. Но опять же — см. мое предыдущее сообщение — класс был описан довольно рано в книге и много не использует. Добавим в Ваш код Executor-а и всё будет в ажуре.

class CCC {

    private final ConcurrentHashMap<String, Future<Void>> cache = new ConcurrentHashMap<String, Future<Void>>();
    private static final ExecutorService exec = Executors.newFixedThreadPool(2);

    public static void main2() throws Exception {
        final CCC ccc = new CCC();

        Thread impatientThread = new Thread() {
            public void run() {
                System.out.println("Impatient client requested for data");
                try {
                    ccc.compute();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        System.out.println("Starting impatient client. ThreadId=" + System.identityHashCode(impatientThread));
        impatientThread.start();

        for (final int i : new int[] {1, 2}) {
            new Thread() {
                public void run() {
                    System.out.println("Starting client " + i + ". ThreadId=" + System.identityHashCode(this));
                    System.out.println("Client " + i + " requested for data");
                    try {
                        ccc.compute();
                        System.out.println("Client " + i + " received response");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }

        Thread.sleep(1000);
//        System.out.println("Impatient client cancels request for data");
//        impatientThread.interrupt();
        exec.shutdown();
    }

    public Void compute() throws InterruptedException {
        while (true) {
            Future<Void> f = cache.get("1");
            if (f == null) {
                Callable<Void> eval = new Callable<Void>() {
                    public Void call() throws InterruptedException {
                        System.out.println("Computation started");
                        Thread.sleep(3000);
                        System.out.println("Computation ended");
                        return null;
                    }
                };
                System.out.println("1");
                Future<Void> ft = exec.submit(eval);
                System.out.println("2");
                f = cache.putIfAbsent("1", ft);
                if (f == null) f = ft;
            }
            try {
                Thread.sleep(1000);
                System.out.println("Print 1000, threadId=" + System.identityHashCode(Thread.currentThread()));
                Thread.sleep(1000);
                System.out.println("Print 2000, threadId=" + System.identityHashCode(Thread.currentThread()));
                Thread.sleep(1000);
                System.out.println("Print 3000, threadId=" + System.identityHashCode(Thread.currentThread()));
                Thread.sleep(4000);
                System.out.println("Print 4000, threadId=" + System.identityHashCode(Thread.currentThread()));

                return f.get();
            } catch (CancellationException e) {
                System.out.println("CancellationException, threadId=" + System.identityHashCode(Thread.currentThread()));
                cache.remove("1", f);
            } catch (ExecutionException e) {
                cache.remove("1", f);
                System.out.println("ExecutionException, threadId=" + System.identityHashCode(Thread.currentThread()));
            }

        }
    }
}



Название метода main я исправил — мне его так проще было из существующего проекта вызвать. Добавил Executora (тут в его конструктор можно и 1 было передать), подправил код создания объекта Future. Добавил отладочные печати через каждую секунду перед вызовом Future.get. Ну и добавил код гашения потока пула потоков executor-а (не понимаю, почему не гасится сам).
Получил такой вывод на экран:

Starting impatient client. ThreadId=5678233
Impatient client requested for data
1
2
Starting client 1. ThreadId=14746332
Client 1 requested for data
Computation started
Starting client 2. ThreadId=8568863
Client 2 requested for data
Print 1000, threadId=5678233
Print 1000, threadId=14746332
Print 1000, threadId=8568863
Print 2000, threadId=5678233
Print 2000, threadId=14746332
Print 2000, threadId=8568863
Print 3000, threadId=5678233
Computation ended
Print 3000, threadId=14746332
Print 3000, threadId=8568863
Print 4000, threadId=5678233
Print 4000, threadId=14746332
Client 1 received response
Print 4000, threadId=8568863
Client 2 received response


Обрати внимание, что "1" и "2" выводятся подряд — значит exec.submit(eval) запускает отдельный поток. Дальше все потоки параллельно продвигаются к финишу (в Print XXXX все 3 Id потоков).
А по сути согласен с тем замечанием, что в примере вычисления шли НЕ параллельно.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.