Блох: Скажу больше: думаю, у него лучшие средства, чем у любого другого языка. Интересно, что сейчас часто слышатся разговоры о смерти Java. Мне кажется, все это несерьезно. Между прочим, лучшие блоки для реализации многопоточности сейчас есть именно в Java. И язык готов пережить небольшое возрождение. Не знаю, куда мы зайдем в ближайшие двадцать лет и лучшее ли это средство для работы с многоядер-ностью. Но из того, что есть сегодня, Java на голову выше своих конкурентов.
Сейбел: Что это за конкуренты?
Блох: Как я считаю, C++ и С#.
Сейбел: А как насчет Erlang или транзакционной памяти?
Блох: Насколько я знаю, транзакционной памяти в рабочем виде пока еще нет ни в одном из крупных языков. Если окажется, что это того стоит, то, наверное, она появится и в Java не позже, чем в прочих языках.
У Erlang свой подход к параллелизму - акторы: если они окажутся успешной находкой, то могут быть реализованы во многих языках. Как вы знаете, Одерски с компанией уже реализовали их в Scala. Пока я не уверен, что акторы - лучшее из придуманного для многоядерного параллелизма, но если все же это так, то и в Java вы скоро увидите их.
Сейбел: Итак, Java, по вашим словам, имеет блоки, позволяющие получить портируемый доступ к параллельным потокам, предоставляемым операционной системой, а также конструкции более высокого уровня в рамках API Java.util.concurrent. Но все равно, это ведь средства довольно низкого уровня в сравнении с Erlang или транзакционной памятью?
Блох: Не уверен. Некоторые конструктивные блоки в Java действительно низкоуровневые, например Atomiclnteger; есть среднеуровневые, например CyclicBarrier, и наконец высокоуровневые - ConcurrentHash-Мар и ThreadPoolExecutor. Уверен, что транзакционная память и акторы найдут свое место в наших конструктивных блоках для многопоточных задач, как только народ убедится, что эти новинки работают хорошо. Если, конечно, они будут работать хорошо.
Некоторые виды транзакционной памяти могут получить значение в будущем, например конструктивные блоки для разработчиков параллельных библиотек. Но мне кажется, транзакционная память не избавит создателей приложений от заботы о блокировках. Она не введет нас в счастливый мир, где потоки не мешают друг другу.
Тому есть несколько причин. Первая из них состоит в том, что если вы пытаетесь делать автоматическую блокировку или оптимистичное управление параллелизмом, основываясь только на чтении и записи на уровне байтов, то между потоками происходит “мнимый конфликт”: физические конфликты не соответствуют логическим. Если вам нужны блокировки, то убедитесь, что захвачены лишь те, которые помогают решить логические конфликты.
Так, например, если у вас есть два потока и оба прибавляют значения для счетчика, они должны выполняться параллельно. Они могут обращаться к одному и тому же участку памяти, но при этом не конфликтуют в логическом плане. Если один поток считывает значение счетчика, а другой увеличивает его, то есть конфликт. Но ведь у вас может быть множество потоков, которые считывают значения, и других, увеличивающих их. Я пока не видел систем, которые могли бы справляться с такими вещами самостоятельно. Может быть, мой пример несколько искусственный, но часто физические ограничения намного суровее логических.
Вторая проблема с транзакционной памятью в том, что внутри нее осуществляются не все операции, например операция ввода/вывода. А вот третья проблема: некоторые виды транзакционной памяти позволяют “обреченным транзакциям” видеть память в неустойчивых состояниях - потенциально это крайне опасно. С этими проблемами мы уже сражались, когда строили транзакционные системы общего назначения. Решения есть, но все они усложняют систему или снижают скорость работы.
Так или иначе, насколько мне известно, транзакционная память еще не вышла из стадии исследований. Прекрасно, что этим занимаются. Но по-моему, она не решит разом все проблемы параллельности - по крайней мере, в обозримом будущем.
Сейбел: Сменим тему. Каков ваш стиль работы в команде?
Блох: Я довольно уживчив, предпочитаю “приятельское программирование” - когда работаешь вместе с кем-то, но не за одной клавиатурой. Вы пишете разные части программы, обмениваетесь кодом. Можно вообще пребывать в разных полушариях. Мы с Дугом Ли таким образом плотно работали несколько лет. Один писал интерфейс, другой говорил: “Все отлично, но я поправил там кое-что, вот погляди”.
Наконец получался интерфейс, который нас устраивал. Я реализовы-вал однопоточную версию, Дуг - многопоточную, во время работы мы обнаруживали разные просчеты и снова поправляли интерфейс. Мы читали код друг друга, Дуг обычно говорил: “Ты можешь сделать вот так - все заработает гораздо быстрее”, - а я отвечал: “Дуг, это ты можешь”. Он был очень силен во всем, что ускоряло работу системы, - виртуальные машины были для него как друзья. Этот вид программирования я очень люблю, он как бы сам подталкивает к удаленному сотрудничеству.
Мне нравится сидеть с кем-нибудь за одним терминалом и работать над кодом, но таким образом я сделал не много программ с нуля. Обычно это случается, когда я делаю ревизию кода; если вижу, что код надо сильно править, то предлагаю человеку посидеть вместе. Это полезно во многих отношениях, например как средство обучения, способ передать знания старшего поколения младшему.
А работать совсем в одиночку мне не нравится. Когда я пишу программу и обдумываю какое-нибудь сложное проектное решение, мне нужно с кем-то советоваться. Везде, где я работал, рядом был кто-нибудь, с кем я мог поделиться. Для меня это очень важно - обратная связь.
Сейбел: Так что же важнее - обратная связь или просто шанс проговорить задачу вместе?
Блох: И то и другое. Мы делаем очень хитрые вещи - часто есть не одно правильное решение или одно, но которого никто до тебя не нашел. Надо полагаться на свой инстинкт, но иногда полезно выслушать того, кто смотрит на вещи по-другому.
Я знаю тех, кто любит программировать в вакууме, но это, по-моему, им вредит. Надо замечать ошибки как можно раньше, выявлять недостатки структуры, пока они не отразились на коде. И если вы экспериментируете с разными подходами или просто с разными свойствами, то нужно обсуждать их с другими. При этом не стоит слепо верить никому - мнения могут быть разными, а за свою работу отвечаете только вы.
Сейбел: Еще один вечный вопрос - кажется, его еще в 1970-е поднимал Вайнберг в “The Psychology of Computer Programming”, - дискутируется и сейчас: должен ли кодом владеть один человек, и только он и должен с ним работать, - или им должны владеть все, кто работал над проектом, и всем должно быть разрешено вмешиваться в него?
Блох: По-моему, владение кодом отрицать нельзя. Это похоже на рождение ребенка: вы даете жизнь коду, и он
Конечно, для компании плохо, если кодом владеет один человек. А если он покинет компанию? Поэтому важно, чтобы сразу несколько программистов знали каждый фрагмент кода и могли с ним работать. Но мечтать о том, чтобы все владели всем кодом, по-моему, не стоит.
Это возвращает нас к теме сфер компетенции. Писать код, перемалывающий биты, способны немногие, и если вы оказываетесь внутри кода, работающего с битами, поговорите с тем, кто умеет обращаться с ним, если сами не умеете. Умелые программисты это любят. Они могут днями работать над тем, чтобы сократить последовательность инструкций на одну или доказать какую-нибудь идентичность, чтобы ускорить вычисления. Но испортить код очень легко. И очень легко написать что-нибудь, что станет хорошо работать, скажем, при 232 - 1 из 232 потенциальных вариантов ввода. Модульный тест может выявить, что ваше решение не работает с этим одним значением, а может и не выявить. И тогда вы будете виноваты в том, что испортили код.
Сейбел: Если говорить о запутанном коде, я заметил вот что: у слишком умных - в определенном смысле, по крайней мере - программистов получается самый плохой код. Они держат все у себя в голове, и в итоге получается не код, а тарелка спагетти.