Генерация Kerberos заголовка

Недавно понадобилось мне получить Kerberos токен в джаве. До сих пор я имплементил только серверную часть Kerberos аутентификации. И там токет брался из заголовка запроса, что приходил из браузера. То есть меня не парило кто его генерирует. Как ни странно, но вменяемого описания как получить токен я не нашёл на просторах интернета. Пришлось лепить самому. Получилось такое:

Чтобы запустить это приложение нужна одна зависимость:

А также создать файл login.conf:

и запускать приложение с параметром -Djava.security.auth.login.config=login.conf

Замечание

Из переменной siteUrl будет вытягиваться хост. И он должен совпадать с хостом в принципале того сабжекта, который будет аутентифицировать токен.

ArrayList не совсем Array

Недавно столкнулся с необычным поведением у java.util.ArrayList. A именно метод java.util.List.add(int, Object). Предлагаю рассмотреть на примере:

данный код свалится с эксепшеном

Но этот же код отлично отработает с нативным массивом:

Казалось бы в чем разница? Может ArrayList инициализируется по своему? Проверил, все в порядке создается настоящий внутренний массив размером который указывается в конструкторе:

Остаётся метод add:

Как мы видим сразу же идёт проверка диапазона:

Другими словами когда мы используем конструктор java.util.ArrayList.ArrayList(int) мы как бы получаем доступ до внутрянки, одновременно с этим ничего больше не получаем взамен. На мой взгляд несколько намешано в архитектуре. Выставлен конструктор только лишь для того, чтобы оказать влияние на внутреннее предствление. И всё.

Так что в очередной раз можно утвердится в мысли, что ArrayList в Java это больше список нежели массив…

Зри в корень: java.lang.Object

В Java в вершине иерархии классов лежит класс java.lang.Object. Лежит и лежит, долгое время я им совсем не интересовался.

На собеседованиях часто спрашивают, какие в нем есть методы, поэтому они как-то сами собой выучились. Пришло время посмотреть на этот класс более внимательно. Первый вопрос, который у меня возник, есть ли вообще в исходниках Java класс java.lang.Object. Он же ведь необычный, он вполне может быть жестко зашит в реализацию, как самый верхний.

Однако, такой класс есть и я приведу тут исходники java/lang/Object.java, опустив javadoc, и попытаюсь пролить свет на некоторые моменты связанные с реализацией jvm:

Что бы я хотел отметить в этом коде?

Всего в Object 11 публичных методов, 5 обычных и 6 с нативной реализацией.

Рассмотрим обычные методы, так как их код уже доступен.

По дефолту все объекты сравниваются на равенство ссылок. Мне, кстати, в своем время понравилась шутка про то, что для того, чтобы запутать C++ программистов указатели в Java названы ссылками.

toString тоже не содержит ничего необычного, кроме разве того, что hashCode() преобразуется в шестнадцатеричную строку. И если бы apangin не написал, что нынче как только нельзя посчитать hashCode, я бы подумал, что раньше Java программисты могли найти свой объект по hashCode, т.к. он был не чем иным как ссылкой. Те 32 битные времена для многих прошли, и теперь даже не знаю, есть ли смысл в toString() выводить hashCode.

Кроме того, что wait относится к примитивам обеспечивающим многопоточность, хочется отметить бесполезность параметра nanos.

В некоторых случаях он просто добавляет одну милисекунду. Интересно, это закладка на будущее или уже есть системы в которых у wait(long timeout, int nanos) другая реализация.

Завершает парад обычных методов в java.lang.Object:

Этот метод ничего не делает, и есть куча материалов о том, что следует избегать его использования finalize и Finalizerсмысл finalize.

Теперь посмотрим на на java/lang/Object.class Например, мне интересно что в нем указано в качестве супер класса. Находим в установленном jre или jdk rt.jar, распаковываем:

И видим, что в super class у него прописаны 00 00, интересно что будет, если руками создать class файл без супер класса.
Я взял Hello.class из моей предыдущей заметки.

Открыл его в vim и заменил содержание буфера на hex дамп vim.wikia.com/wiki/Hex_dump:

Поразился мощи vim редактора. Быстренько нашел байты для super_class. Напомню, они лежат согласно спецификации через 4 байта после окончания constant_pool. Конец constant_pool ищется по тегу строки 00 01 и последовательности не нулевых байтов, когда начинаются нули идут другие разделы constant_pool. Для других class файлов это может быть не так, но в моем случае сработало.
Возвращаемся обратно к бинарному виду:

Сохраняем изменения. Запускаем наше поправленное приложение:

Ошибка, да еще не какая-нибудь, а выброшенная из нативного метода во время загрузки классов. Пойдем разбираться, за одно может поймем как выбрасывать такие ошибки.

Нам нужны исходники jdk. Я выбрал OpenJDK для исследования. Будем качать их отсюда:

hg.openjdk.java.net/jdk8/jdk8

И хранить в Меркурии:

hg clone hg.openjdk.java.net/jdk8/jdk8

Но на этом не всё.

Надо еще запустить:

И подождать. Отлично, исходники скачались и можно искать нашу ошибку. Я делаю это grep-ом:

Открываем classFileParser.cpp и там на 3095 строчке:

Нас интересует вот эта часть:

check_property лежит в заголовочном файле classFileParser.hpp и выглядит так:

Я стал искать где выставляется _need_verify и за что отвечает. Оказалось в classFileParser.cpp есть вот такая строчка:

verify передается при вызове:

Этот метод вызывается во многих местах, но нас интересует в hotspot/src/share/vm/classfile/classLoader.cpp:

Как же устроен should_verify_for в hotspot/src/share/vm/classfile/verifier.cpp:

Так как в should_verify_class мы передаем false, смотрим BytecodeVerificationLocal в hotspot/src/share/vm/runtime/arguments.cpp:

Зарываясь дальше можно найти черную магию с макросами в hotspot/src/share/vm/runtime/globals_extension.hpp:

Но меня это пока не интересует. Мне надо выяснить значение BytecodeVerificationLocal, в случае когда jvm стартует без параметра -Xverify. Это можно найти в коде, но мне кажется, сейчас не уместным лезть дальнейшие дерби и пора выбираться. Документация в помощь. По дефолту jvm запускается с параметром -Xverify:remote и BytecodeVerificationLocal будет false.

Значит _need_verify тоже false и в check_property вызывается assert_property(property, msg, index, CHECK) с параметрами false, «Invalid superclass index %u in class file %s», 0, CHECK_NULL.

Собственно, здесь и выбрасывается сообщение об ошибке. Теперь посмотрим на fatal(msg), чтобы узнать как это делается.
Хотя, на часть вопроса мы уже ответили. Нельзя сделать classfile в котором для поля super_class будет значение 0 и загружать его с помощью дефолтного ClassLoader.

Итак, fatal определенный в hotspot/src/share/vm/utilities/debug.hpp:

hotspot/src/share/vm/utilities/debug.cpp:

Реализация report_and_die() в hotspot/src/share/vm/utilities/vmError.cpp нетривиальна, но из нее следует, что в Java мы уже не возвращаемся и выводим сообщение об ошибке из недр jvm. На этом я хочу переостановить исследование jvm и java.lang.Object.

Выводы

java.lang.Object особый класс, имеющий уникальный class file, в котором в качестве суперкласса не указан ни один класс. Создать такой же класс средствами языка Java нельзя, но также затруднительно, если вообще возможно, сделать это и манипуляциями с байтами class файла. Надеюсь у меня получилось передать часть восхищения, которое я испытывал исследуя исходники jvm. Призываю всех попробовать сделать то же самое.

Источник

3D NAND – что это. Разбираемся с преимуществами технологии

В одном из предыдущих материалов мы «пробегались» по типам памяти, используемой в SSD-накопителях. Разбирались, в чем отличия MLC от TLC, какие у каждого типа достоинства и недостатки. Но это все была технология планарной памяти, а в тренде сейчас многослойность и третье измерение. 3D NAND – что это такое? Какие у него преимущества, перспективы и, вообще, оно нам надо? Давайте разберемся.

Почему планарная память так называется

В последние годы актуальной задачей стало создание емких, быстрых, надежных и компактных хранилищ данных. Смартфоны, планшеты, фото- и видеоаппаратура, прочая мобильная и не очень техника и, конечно же, бурно завоевывающийся рынок SSD-накопители. Требуются именно емкие и небольшие по размеру микросхемы памяти, учитывая ограничения, которые предъявляют некоторые твердотельные диски. Достаточно посмотреть на форм-фактор M.2 чтобы понять, что большого количества чипов на этой маленькой платке разместить действительно негде.

До некоторого времени увеличивать емкость можно было как минимум двумя способами:

  1. Увеличить количество бит, хранящихся в ячейке памяти. Так появилась MLC (2 бита в ячейке», потом ее активно стала вытеснять TLC (уже 3 бита на ячейку).
  2. Уменьшить физический размер ячейки, для чего использовались все боле тонкие техпроцессы. Так, на смену 32 нм техпроцессу пришел 24 нм, его сменил 19 нм, последний, используемый сейчас, техпроцесс – это 15 нм.

Для увеличения емкости кристалла используют оба способа, но дело в том, что последний, 15 нм техпроцесс, действительно последний, т. к. достигнут технологический предел уменьшения физического размера ячеек, и 15 нм действительно является последним техпроцессом, по которому производят привычную NAND-память.

Что собой представляет NAND-память

Если рассмотреть архитектуру памяти, то единицей хранения информации является транзистор. Традиционно используются транзисторы с плавающим затвором, в котором и хранится один, два или три бита информации. Количество этих битов зависит от типов памяти, о которых можно прочитать в другом материале.

SHemaУпрощенная схема NAND-памяти представлена на рисунке. Ячейки (они же транзисторы) соединяются последовательно по 16 или 32 ячеек в группе, образуя страницы, из которых формируется блок. Можно представить себе этакое плоское поле, все утыканное ячейками памяти.

Один из недостатков такой организации памяти – в необходимости оперировать не отдельными битами или байтами, а блоками данных, т. е. произвольный доступ к отдельной ячейке невозможен. Если в случае чтения это не является проблемой, то с записью возникают сложности. Для изменения одного бита приходится считывать блок данных, изменять его и записывать обратно.

Это требует выполнения определенных действий (и времени) по программированию ячеек при записи. Причем перезаписываются даже те ячейки, которые не изменялись. Отсюда и вытекает ограниченность количества циклов перезаписи, о которой часто говорят применительно к твердотельным накопителям. Особенно актуально это стало в связи с массовым распространением трехбитовых (TLC) ячеек. Что ж, ради снижения стоимости чипов памяти приходится чем-то жертвовать.

Подобное соединение ячеек позволяет плотно разместить их на кристалле, чем достигается высокая емкость чипов памяти. Чем больше информации можно разместить на единице площади кристалла, тем ниже себестоимость конечного продукта, в данном случае – SSD-диска.

Как было сказано, бесконечно уменьшать размер ячеек нельзя, как и увеличивать плотность их расположения. 15-нм техпроцесс подошел к тому пределу, когда двигаться дальше уже некуда. Ячейки настолько малы, что при дальнейшем их уменьшении заряд начнет «перетекать» из одной ячейки в другую, что, естественно, недопустимо.

3D NAND – что это, спасение?

Можно сказать, что да. Если стоимость кристалла памяти зависит от его размера, а уплотнять его уже не представляется возможным, то почему бы не перейти от двумерной (планарной) организации ячеек к трехмерной, развернув их вертикально? В этом фундаментальное отличие 3D NAND от старой, «плоской» системы размещения ячеек.

Ячейка в данном случае имеет форму цилиндра, в котором внешний слой – это управляющий затвор, внутренний – изолятор и между ними слой, хранящий биты информации слой. Эти цилиндры размещены вертикально, образуя стек, это позволяет убить сразу не одного зайца. Мало того, что существенно возросла емкость кристалла, так еще и появилась возможность откатиться немного назад, вернувшись на более «толстые» техпроцессы, снизив взаимовлияние соседних ячеек друг на друга и риск перетекания заряда из одной ячейки в другую.

3dvnandevolution_041714282246_640x360Первой такую память сделала компания Samsung, назвав ее V-NAND (V – от слова vertical, вертикальная). Первое поколение имело 24 слоя, второе – 32, а в последнем, третьем поколении используются уже 48 слоев. Компании Micron, Toshiba представили свои чипы памяти позже, и производят их уже с 64-мя слоями.

Причем, наблюдается и разница в подходах к архитектуре этих микросхем и расположению их на кристалле.

Micron располагает управляющие элементы под NAND ячейками, что экономит место на кристалле, позволяя увеличить его емкость. Мало того, хотя Samsung и Toshiba отказались от технологии плавающего затвора, воспользовавшись технологией CTF (Charge Trap Flash), которая использует изолированную область для хранения заряда (именно изолированность позволяет снизить утечки, повысить надежность памяти), в Micron остались верны плавающему затвору.

toshiba-nand-f1В Toshiba управляющие элементы расположены в верхней части, что, по мнению компании, позволяет этим элементам меньше подвергаться нагреву. К тому же линии ячеек как бы свернуты, напоминая букву «U», а не расположены в одну линию. Все это позволяет добиться снижения количества ошибок при операциях чтения/записи. Ну и, как было сказано чуть выше, используется технология CTF. Сама Toshiba называет свою трехмерную память BiCS 3D NAND (Bit Cost Scalable).

В общем, подходы разные, и что лучше или хуже – будет ясно после того, как появится достаточное количество накопителей с чипами памяти разных производителей, которые можно будет сравнить, устроив тестирование, накопится определенная статистика использования.

Итак, трехмерная память сняла остроту необходимости утончать техпроцесс, как один из способов увеличения емкости чипов. Правда, при этом возникли некоторые другие технологические сложности, которые, судя по бодрым анонсам практически всех чипмейкеров, успешно преодолеваются. Так, SK Hynix планирует в скором времени перейти на производство 72-слойных чипов. Та же Toshiba отлаживает выпуск 64-слойных чипов, предлагая их сейчас с емкостью 256 Гб (32 ГБ), а в скором времени ожидается выпуск 3D NAND чипов с емкостью 512 Гб (64 ГБ).

Судя по всему, второе полугодие обещает быть интересным. Увеличится емкость чипов, будут предложены кристаллы с бОльшим количеством слоев.

Что такое технология CTF

В чем суть этой технологии? Разница заключается в области, в которой хранится заряд, и материала, из которого эта область выполнена. Классический транзистор с плавающим затвором, помимо обычных стока, истока, и затвора, называемого в данном случае «управляющим затвором», имеет и еще одну область – расположенный в слое диэлектрика проводник, называемый «плавающим затвором», в котором, собственно, и накапливается заряд. В нем-то и хранятся биты данных. В качестве диэлектрика используется диоксид кремния SiO2.

CTFТранзистор, выполненный по технологии CTF (Charge Trap Flash) сделан несколько иначе. Собственно, область, где хранится заряд, выполнена из нитрида кремния Si3N4, обладающего рядом отличительных свойств. Так, являясь, по сути, диэлектриком, этот материал способен хранить заряд, что позволяет использовать его в качестве запоминающей ячейки.

По сравнению с диоксидом кремния (SiO2), бОльшая концентрация электронных и дырочных ловушек нитрида кремния как раз и позволяет использовать материал для хранения данных.

При этом такой параметр, как диэлектрическая проницаемость у нитрита кремния (Si3N4) выше, чем у диоксида кремния — 7 против 3.9, что позволяет снизить токи утечки и более надежно хранить заряд.

Отсюда становится понятной аллегория, озвученная лидером в разработке 3D NAND памяти, компанией Samsung, что транзисторы с плавающим затвором – это вода, а с ловушкой заряда – это сыр. Плавающий затвор (вода) слабо препятствует перемещениям зарядов и их попыткам вообще покинуть эту область (утечка), в то время как ловушка заряда подобна «сыру», существенно ограничивающая возможность этих перемещений и попытки вырваться на «свободу».

Среди достоинств этой технологии обычно указывают:

  • Увеличение надежности и упрощение хранения нескольких бит в одной ячейке.
  • Упрощение производства за счет уменьшения количества технологических операций.
  • Меньший размер ячейки.
  • Более высокий процент выхода годных.

Различают несколько вариаций изготовления слоев транзистора в зависимости от материалов:

  • SONOS – Silicon-Oxide-Nitride-Oxide-Silicon.
  • MONOS – Metal-Oxide-Nitride-Oxide-Silicon.
  • TANOS – Titanium-Alumina-Nitride-Oxide-Silicon.
  • THNOS – Titanium-high-k dielectric-Nitride-Oxide-Silicon.

Где предел 3D NAND?

Ну хорошо, количество слоев памяти растет, а где предел, не получится ли так, что вскорости будет достигнут лимит количества слоев, и придется искать альтернативы? Ответ кроется в технологических проблемах и способах их решения.

Samsung-Stacking-transistor-cellsЕсли вкратце, и очень упрощенно, то производство многослойной памяти заключается в напылении n-го количества слоев на кремниевую пластину, образующие линии слов (word line), а другая операция заключается в травлении огромного количества отверстий (high aspect ratio etch) через эти слои, чтобы впоследствии сформировать линии битов (bit line). В пространстве линии слов и битов ориентированы перпендикулярно друг другу, а главные сложности кроются именно в отверстиях.

Еще пару лет назад заявлялось, что есть технологические проблемы с травлением отверстий в слоях, количество которых достигает 60-70. Правда, сейчас, когда 64-слойная память – уже реальность, а на горизонте 72-слойная, и есть разговоры про более многослойные варианты, с этой проблемой удается справиться. Вопрос, как?

Один из вариантов – технология «string stacking». Если не вдаваться в технические подробности, то это установка отдельных чипов памяти (которые сами по себе многослойные) друг на друга (стекирование) с последующим соединением таким образом, чтобы этот многослойный бутерброд распознавался как единое целое, как одна микросхема. Таким образом, использовав чипы 3D NAND с 32-мя слоями, можно получить итоговый чип с 64 (2 слоя чипов), 96 (3 слоя чипов) и т. д. слоями. Но и тут есть сложности технологического порядка, в первую очередь связанные именно с соединением и коммутацией чипов, которые находятся на этапе решения.

StackingЕще один момент – а сколько вообще слоев может быть? Где предел, при котором микросхема не станет слишком толстой? Если рассматривать с теоретической точки зрения, то можно провести следующие грубые прикидки.

Высота слоев 32-слойной 3D NAND от Samsung составляет около 4 мкм. При этом полупроводниковые пластины, используемые в производстве микросхем, имеют толщину 625-775 мкм в зависимости от диаметра. Одним из завершающих этапов производства чипов (правда не всегда используемый) является сошлифовывание (back-grinding) обратной стороны этой пластины до толщины порядка 50-75 мкм. Это уменьшает размеры кристалла и облегчает упаковку готовой микросхемы в корпус, да и для стекирования чипов подходит как нельзя лучше.

Если взять толщину 32-слойного чипа памяти и толщину 300-мм пластины, которая составляет 775 мкм, то, в теории, можно уложить более 190 слоев чипов памяти прежде, чем их толщина превысит толщину исходной пластины (775 / 4 = 193.75).

Конечно, это только в теории, и, скорее всего, таких значений достигнуто не будет, но это иллюстрирует, что «запаса прочности» у технологии 3D NAND вполне достаточно. Главное – решить текущие технологические проблемы именно с укладкой чипов друг на друга и их соединением. Если же это будет выполнено, то количество слоев (чипов) может исчисляться десятками и сотнями, а количество слоев ячеек может достигать многих сотен. Возможную емкость подобных микросхем попробуйте посчитать самостоятельно.

Либо искать решение проблемы с травлением отверстия в многослойных кристаллах. В конце концов, подробностей о том, как выполнены представленные 64-слойные чипы, а также уже анонсированные 72-слойные, нет. Возможно, удалось все же найти разобраться с травлением, либо присутствуют какие-то другие решения.

В общем, в теории перспективы вполне радужные, что будет на практике?

Проблема параллелизма операции чтения/записи

Увеличение емкости, несомненно, благо, т. к. в небольшом форм-факторе (в том же M.2) можно получить накопители объемом в несколько терабайт. Вот только возникла одна проблема: при высокой емкости чипов становится сложным распараллелить операции чтения/записи. В первую очередь это касается накопителей небольшого объема.

Intel 600pЭто хорошо характеризует такой печально известный своей низкой производительностью накопитель Intel 600p. Дело в том, что в нем используются чипы памяти емкостью 384 Гб (48 ГБ) производства Micron, и для того, чтобы получить емкость накопителя в 128 ГБ, надо всего 3 такие микросхемы. Для 256-гигабайтного накопителя используются 6 микросхем и т. д.

Казалось бы, меньше микросхем – больше места для их размещения. Это так, но большинство контроллеров, особенно в сегменте производительных моделей, имеют 4 или 8 каналов, обеспечивающих параллельный доступ к памяти. Если микросхем памяти 3 (6, 9…), то как задействовать все доступные каналы? В том то и дело, что никак. Вместо использования всех 8-ми (или 4-х) каналов приходится ограничиваться использованием только шести (3-х). Получается, что контроллер работает не на полную мощь, отсюда – падение производительности.

В общем, вырисовывается некоторая проблема именно с накопителями низкой емкости. Возможно, стоимость их будет невелика, но и скоростные показатели будут там же. Получается, что если хочется скорости, то пожалуйте приобретать более емкие накопители. А стоимость?

Заключение. 3D NAND – это то, с чем нам жить

Ни для кого не секрет, что за 3D NAND будущее, и в самом ближайшее время начнется (если уже не началось) активное вытеснение планарной памяти. Все будет зависеть от стоимости решений, производственных возможностей производителей, в первую очередь Micron, Toshiba, и, возможно, SK Hynix, если дело двинется дальше анонсов. Про Samsung говорить нечего, т. к. свои чипы 3D памяти они, фактически, никому не поставляют.

Думается, бюджетные SSD-накопители продержатся еще какое-то время, а вот производительные решения, и, в первую очередь, твердотельные диски, работающие на шине PCIe, будут активно мигрировать именно на 3D NAND.

Источник

Load your CLASSPATH at runtime

One of the great advantages of programming in JAVA is the ability to add to your code as you go. You can create classes and turn them into packages which can then be imported and used in any other JAVA application. Each instance of your program relies on the CLASSPATH environment variable for external packages. For example you can import an external JDBC driver for your application to connect your database. You can also import dynamic modules to add and change program functionality. Setting the classpath variable can be done as a part in the environment variables or while invoking the JVM. However, when your program relies on external web updates, it’s not practical to change the command arguments every time there is a major update. In this article, we are going to explore programmically changing your classpath to modify program features. We are going to assume you have an updater module that basically drops all of your JAR files into the module’s directory. We are then going to read all the files in the directory and add them to your classpath at run time. The code to reading all the files in the directory looks like this:

And the last step is to append the files to the classfile.

It is very important to program your classpath as the first step when you invoke your program. Any objects that are being initialized before the classpath is set can result in a no class definition error. You’ve seen how useful it can be to any software that relies on external packages. The possibilities are endless. Happy programming!

Source

Как я писал Android приложение

Не думал, что захочется поделиться опытом по написанию моего первого функционального Android приложения. Когда начинал его писать, думал, что всё будет просто, ведь, блин, их уже то понаписано тьма! Однако мне пришлось спотыкаться на ровном месте, причём не раз!

Итак, сменил я зимой сего года место работы. На новом месте свой интернет, свои “злые” айтишники и подумалось, что негоже мне светить в траффике урл сайта где я новости читаю. Скажем так, не популярен он в нынешнем обществе. Соответственно, приложение в Android самый логичный выход. Там может быть мобильный интернет и никто ничего с работы не увидит.

Стартап за 2 дня

Опыта работы по написанию приложений для Android у меня не было. Вернее, ещё в 2011, помнится, пытался баловаться плагинчиком для Eclipse. Но много лет прошло. Плагинчик уже не модный, а модна Idea. Вот и пришлось тыкать в новые кнопки и в новых окошках (не люблю я Idea). Тут же можно добавить всякоразные там проблемы с Android API, инициализацией проекта в IDE и другой сопроводительной активностью.

Тем не менее, за два дня функционал был готов. Я мог загружать список всех новостей и просматривать отдельную из них. С меня потребовалось всего лишь дамп HTML с сайта и его парсинг. Google дал информацию как это всё организовать в приложении. Ну а новостной сайт позволял это своей архитектурой.

Немножко напильником

Само собой, нужно было слегка подпилять, что было сделано за два дня. Я особо не заморачивался с красивостью кода, тестами и прочими тормозами стартапа. Пришлось всунуть jsoup  для парсинга HTML, когда сабстрингать стало уже больно. Повозился с картинками, чтобы их ширина выравнивалась под размер экрана. Вырезка ненужного HTML и рекламных ссылок. Плюс один юнит тест. Неделя ушла на всё это.

Всё работает, но нет

В итоге у меня есть приложение, которое работает в эмуляторе. И даже впихнул его себе в телефон. Но вылезла беда. Если в сети WiFi оно ещё работает более-менее, то в мобильной сети оно совсем отказывалось вменяемо работать. Вернее, могло грузить список новостей несколько минут и то, не было гарантии, что все прогрузится. Причем речь шла о нескольких киллобайтах текста. Начались наблюдения. Оказалось, что у одного оператора тупит, у другого оператора почти не тупит. Примерно та же ситуация наблюдалась и при попытке зайти на сайт через браузер. 3G, 4G никакой роли не играли. На прямой вопрос у оператора: вы, уроды, сниффите? Ответ: нет конечно же! как вымогли подумать такое! Но факт есть факт. На все остальные сайты заходит вменяемо, но вот на мой, с новостями – тупит. Законная сторона меня лично не парит. Во первых, симка предоплатная, во-вторых закон я не нарушаю. Так что, стукачу оператору и рыцарям плаща и кинжала – физкульт привет! Парит меня, что работает никак моя аппликашка! Несколько месяцев я мучался, переключал симки в телефоне, но потом немножко разгрузился с текучкой и вернулся к разработке. Первым делом нужно было устранить сниффинг. Google плюс мой сайт с поддержкой PHP и проксик готов. Теперь контент грузится на обеих операторах одинаково, а на моём сайте заработал счётчик с посещениями. Ещё раз всем им там привет!

Грузи, грузи

За время, которое прошло, у меня на машине появился Docker. И это чудо не захотело дружить ни с VirtualBox, ни с Android эмулятором. Времени, а главное желания разбираться, в этом гауне не было. Пришлось разработку перенести на другую машину, попутно укрепив ненависть к Docker.

Чем парсишь, Вань

Всё. Новости вроде грузит нормально теперь, но вот если открываешь отдельно взятую – боль! Иногда грузит, но долго, иногда чёрный экран и ничего. Предположение первое – опять сеть. Дебаг мне в помощь и… Не сеть! Парсинг! Пришлось рефакторить парсер и мерять. Получив цифры для jsoup, я начал допиливать его конкурент на сабстрингах. И уже при частичной реализации парсинга я получил цифры 420 против 24. Я  охренел – более чем на порядок отличие! Попытался поискать аналог jsoup, нашёл какой-то noname, но и его цифры (около 120) меня не соблазнили. Кроме того, у него был более тупой API чем у jsoup. Отсюда вынужденное решение – переписывать всё на сабстринги с последующей миграцией в StringBuilder. Уже чувствую вибрацию приверженцев секты “красивый код”, но мне, чёрт возьми, нужно было работающее приложение, а не красивый код внутри хреново рабочего! Много пришлось повозиться со всякими там циклами, скобочками и прочей мелочёвкой. Университет вспоминался часто с курсом основ алгоритмизации! Никаких новых алгоритмов! Упаси бог! Просто – необычные и запутанные циклы с индексами. Много циклов и индексов. Месяц ушёл на всё это.

Ща как запиздячу

Внутренне удовлетворение достигнуто. Но чхать моё приложение хотело на моё удовлетворение. Один хрен криво работало. Иногда новость подгружается весело и задорно, иногда тьма на весь экран, но с большой вероятностью подгрузки. Такое ощущение что ресурс у операционки занят и мой запрос на сервер что-то ждёт где-то в дебрях Android. Воркэраунд для этого вроде очевидный – запилить пул тредов и в фоне загружать новости в кеш, пока я читаю первую новость или просматриваю список новостей. Здесь было две попытки. Первая привела к стохастическому свалу всего приложения без объяснения причин. После того, как обернул код загрузки новости в try-catch-Throwable, проблема исчезла.

Итог

Через пол года у меня есть в телефоне приложение, которое:

  1. Работает.
  2. Меня не бесит.
  3. Я его написал сам.
  4. Есть куда по-чучуть что-то улучшать в будущем.

Мораль

Я не вспомнил о куче мелких “камушков” которые занимали время. Иконка, цвет кнопки, картика “под краешек” экрана и прочая мелочёвка. Хочу сказать, всё оно через Ж делается в Android. Такое вот втутреннее ощущение, что рудимент и рудиментом погоняет. Работа с Android помимо самой Java, требует ещё определённой сноровки и в самом фреймворке. Нельзя снисходительно джавистам отноститься к андройдщикам!

Много я не ожидал. Изначально думал, что всё будет гораздо проще. Например, я так и не докопал можно ли логировать работу приложения, трудности были с выплывающим окошком и т.д. Не ожидал, что будет столько проблем, не связанных с разработкой. По сути много затыков лежали в области внедрения и сопровождения приложения.

До сих пор остаётся открытым вопрос: написал бы я приложение, если бы уделял внимание всякоразным бестпрактисам, юнит тестам и TDD (шепотом и восхищённо)?

Также я бы подчеркнул, что разработка приложения это не есть непосредственно девелопмент с тестированием. Очень большую роль играет деливери и сопровождение. В моём случае, как видно, это были ключевые звенья в разработке. Никакими бестпрактисами я бы не отловил проблемы с мобильной сетью! Так что, как это может ни прискорбно для многих прозвучать, но будущее за “хуяк, хуяк и в продакшн”(c).

 

Вопросы на собеседовании Java

Что такое инкапсуляция?
В общем случае в разных языках программирования термин «инкапсуляция» относится к одной или обеим одновременно следующим нотациям:
– механизм языка, позволяющий ограничить доступ одних компонентов программы к другим;
– языковая конструкция, позволяющая связать данные с методами, предназначенными для обработки этих данных.
ссылка 1

Какие типы ссылок существуют в Java?
Strong reference
Weak Reference
Soft Reference
Phantom Reference
Soft-ссылка, в отличие от weak-ссылки, сможет отложить процесс удаления обьекта до тех пор, пока не появится острая нехватка памяти. Учитывая это отличие soft-ссылки от weak, первая больше подходит для кэшей, а weak для метаданных.
ссылка 1
ссылка 2

Как GarbageCollector очищает ссылки, которые ссылаются друг на друга?
Java GC рассматривает объекты “мусор”, если они недоступны через цепочку, начинающуюся с корня коллекции мусора, поэтому эти объекты будут собраны. Даже если объекты могут указывать друг на друга, чтобы сформировать цикл, они все еще мусор, если они отрезаны от корня.
ссылка 1

Временная сложность алгоритма:
В информатике временна́я сложность алгоритма определяет время работы, используемое алгоритмом, как функции от длины строки, представляющей входные данные. Она обычно выражается с использованием нотации «O» большое.
«O» большое и «o» малое — математические обозначения для сравнения асимптотического поведения (асимптотики) функций.
Линейная сложность подразумевает рост времени выполнения алгоритма пропорционально размеру входных данных. (пример: обход массива)
Константная временная сложность не зависит от размера входных данных. (пример: доступ по индексу к определенному элементу массива)
ссылка 1
ссылка 2

Методы сортировки:
Методы сортировки делятся на две группы: устойчивые и неустойчивые. К устойчивым сортировкам принадлежат:
– Пузырьковая Оn2
– Слиянием O(n logn)
– Вставкой On2
– С помощью двоичного дерева O(n logn)
К неустойчивым:
– Сортировка выбором On2
– Сортировка расчёской On2
– Сортировка Шелла On4/3
– Пирамидальная сортировка O(n logn)
– Быстрая (или Хоара) O(n logn)
ссылка 1

Какая сортировка используются в джаве?
java.util.Arrays.sort(prim[], …) — для сортировки массивов примитивов prim (int, byte, char, и т.д.). Использует алгоритм быстрой сортировки (quick sort).
java.util.Arrays.sort(T[], …) и java.util.Arrays.sort(Object[], …) — для сортировки массивов комплексных объектов произвольного типа T или Object. Использует алгоритм сортировки слиянием (merge sort).
java.util.Collections.sort() — для сортировки коллекций. Использует алгоритм сортировки слиянием (merge sort).
Начиная с 8-ки сортировка слиянием объявлена устаревшей и заменена на TimSort
ссылка 1
ссылка 2
ссылка 3

Всегда ли хеш код String кешируется и вычсляется только один раз?
Не всегда. Если в результате подсчета хеш кода впервые получается 0, то для этого значения хеш код будет пересчитан в следующий раз. (см. реализацию метода hashCode в String)

Какой размер boolean в Java и почему именно такой?
Минимальный размер этого типа 1 байт. Сделано это для удобства размещения значения в памяти и работе с ним. Например, в могопоточной среде.

Why does 128==128 return false but 127==127 return true when converting to Integer wrappers?
To save on memory, Java ‘reuses’ all the wrapper objects whose values fall in the following ranges:
– All Boolean values (true and false)
– All Byte values
– All Character values from \u0000 to \u007f (i.e. 0 to 127 in decimal)
– All Short and Integer values from -128 to 127.
ссылка 1

TreeMap vs Hashmap:
HashMap makes absolutely no guarantees about the iteration order. It can (and will) even change completely when new elements are added.
TreeMap will iterate according to the “natural ordering” of the keys according to their compareTo() method (or an externally supplied Comparator). Additionally, it implements the SortedMap interface, which contains methods that depend on this sort order.
LinkedHashMap will iterate in the order in which the entries were put into the map
ссылка 1
ссылка 2

Для чего нужнен loadFactor в HashMap?
float loadFactor – коэффициент заполнения HashMap. Равен отношению числа хранимых элементов в таблице к её размеру. Является мерой заполнения таблицы элементами, при превышении которой происходит автоматической перехеширование.
Другими словами, когда кол-во элементов в бакете начинает превышать (по умолчанию 75%) от всего количества элементов в таблице, HashMap производит автоматическую оптимизацию. А именно: добавляет еще один бакет и производит перехеширование (переразмещение) всех элементов таблицы. Поэтому, это очень плохо сказывается в том случае, если hashCode ключа всегда постоянный. HashMap не только вырождается в список, но еще присутствует постоянный рост бакетов и перехеширования элементов при добавлении нового.
ссылка 1
ссылка 2

Откуда взялся метод getLast в LinkedList? Ведь в List его нет.
LinkedList имплементирует сразу два интерфейса List и Deque. Другими словами, LinkedList – это и список, и очередь, и двухсторонняя очередь одновременно. Метод getLast пришёл как-раз из Deque.

Что такое Natural ordering?
The natural ordering of two objects a and b is the outcome of a.compareTo(b), i.e. what those objects themselves think which one is smaller, greater or equal to the other object. Normally a.compareTo(a) == 0 and if a.compareTo(b) <> 0 then b.compareTo(a) >< 0. The entire thing melts away when you think of integer numbers, e.g. 3 < 4, so 4 > 3 and 3 == 3
natural ordering ты сам реализуешь в Comparable. Посмотри, Collections.sort. Там два метода: один реализует natural ordering класса, второй – ты создаешь компаратор. А какой из них ты будешь использовать – твое личное дело.
ссылка 1
ссылка 2

Что такое красно-черное дерево?
Красно-чёрное дерево — это одно из самобалансирующихся двоичных деревьев поиска, гарантирующих логарифмический рост высоты дерева от числа узлов и быстро выполняющее основные операции дерева поиска: добавление, удаление и поиск узла.
ссылка 1
ссылка 2

Что такое самобалансирующееся двоичное дерево поиска?
Двоичное дерево поиска — это двоичное дерево, для которого выполняются следующие дополнительные условия (свойства дерева поиска):
1. Оба поддерева — левое и правое — являются двоичными деревьями поиска.
2. У всех узлов левого поддерева произвольного узла X значения ключей данных меньше, нежели значение ключа данных самого узла X.
3. У всех узлов правого поддерева произвольного узла X значения ключей данных больше либо равны, нежели значение ключа данных самого узла X.
Очевидно, данные в каждом узле должны обладать ключами, на которых определена операция сравнения меньше.
ссылка 1

Что такое расширяющееся дерево?
Расширяющееся (англ. splay tree) или косое дерево является двоичным деревом поиска, в котором поддерживается свойство сбалансированности. Это дерево принадлежит классу «саморегулирующихся деревьев», которые поддерживают необходимый баланс ветвления дерева, чтобы обеспечить выполнение операций поиска, добавления и удаления за логарифмическое время от числа хранимых элементов.
ссылка 1
ссылка 2

Что такое сбалансированное дерево?
В этой главе будет рассматриваться другая разновидность бинарных поисковых деревьев – AVL-деревья или сбалансированные двоичные деревья с минимальным временем поиска по дереву. Это свойство AVL-деревьев обеспечивается их сбалансированностью, которая одновременно усложняет алгоритмы для вставки узлов в дерево и их последующего удаления.
ссылка 1

Java 8:
Java 8 (codename: Spider) was released on March 18, 2014, and included some features that were planned for Java 7 but later deferred.
Work on features was organized in terms of JDK Enhancement Proposals (JEPs).
JSR 335, JEP 126: Language-level support for lambda expressions (officially, lambda expressions; unofficially, closures) under Project Lambda and default methods (virtual extension methods) which allow the addition of methods to interfaces without breaking existing implementations. There was an ongoing debate in the Java community on whether to add support for lambda expressions. Sun later declared that lambda expressions would be included in Java and asked for community input to refine the feature. Supporting lambda expressions also allows the performance of functional-style operations on streams of elements, such as MapReduce-inspired transformations on collections. Default methods allow an author of an API to add new methods to an interface without breaking the old code using it. Although it was not their primary intent, default methods also allow multiple inheritance of behavior (but not state).
JSR 223, JEP 174: Project Nashorn, a JavaScript runtime which allows developers to embed JavaScript code within applications
JSR 308, JEP 104: Annotation on Java Types
Unsigned Integer Arithmetic
JSR 337, JEP 120: Repeating annotations
JSR 310, JEP 150: Date and Time API
JEP 178: Statically-linked JNI libraries
JEP 153: Launch JavaFX applications (direct launching of JavaFX application JARs)
JEP 122: Remove the permanent generation
Java 8 is not supported on Windows XP but as of JDK 8 update 25, it can still be installed and run under Windows XP. Previous updates of JDK 8 could be run under XP, but had to be installed after a forced installation by directly unzipping files from the installation executable.
From October 2014, Java 8 was the default version to download from the official website. “Oracle will not post further updates of Java SE 8 to its public download sites for commercial use after September 2018”.
ссылка 1
ссылка 2
ссылка 3

Допустим есть выражение IntStream.of(1, 7, 9).filter(x -> x < 5).map(x -> x + 1).limit(2).forEach(System.out::print). Какой метод итерирует коллекцию?
У стримов есть некоторые особенности. Одна из них: обработка не начнётся до тех пор, пока не будет вызван терминальный оператор. В данном случае это forEach.
ссылка 1
ссылка 2

Чем интерфейсы в Java 8 отличаются от абстрактых классов?
Во-первых, интерфейсы по-прежнему не могут хранить состояние, в отличие от тех же абстрактных классов. Хотя теперь, они напротив могут задавать некое дефолтное поведение. Во-вторых, ограничение на кол-во прямых родителей / наследников у классов как было единицей, так и осталось. Но вот интерфейсов то мы всегда могли имплементить сколь угодно много. И вот он – самый главный чит восьмерки – теперь у нас есть возможность практически полноценного множественного наследования!
ссылка 1
ссылка 2

Что будет если класс имплементирует два интерфейса с одним и тем же методом?
Если это не дефолтный метод – то ничего страшного. Метод будет импелементирован в классе и полиморфно будет вызываться на каждом из интерфейсов.

Что такое Execution Service?
До Java 5 для организации работы с несколькими потоками приходилось использовать сторонние имплеменации пулинга или писать свой. С появлением ExecutorService такая необходимость отпала.
ExecutorService исполняет асинхронный код в одном или нескольких потоках. Создание инстанса ExecutorService’а делается либо вручную через конкретные имплементации (ScheduledThreadPoolExecutor или ThreadPoolExecutor), но проще будет использовать фабрики класса Executors. Например, если надо создать пул с 2мя потоками, то делается это так:
ExecutorService service = Executors.newFixedThreadPool(2);
Если требуется использовать кэширующий пул потоков, который создает потоки по мере необходимости, но переиспользует неактивные потоки (и подчищает потоки, которые были неактивные некоторое время), то это задается следующим образом:
ExecutorService service = Executors.newCachedThreadPool();
ссылка 1
ссылка 2
ссылка 3

Как остановить поток в джаве?
Все потоки нужно прерывать при помощи метода interrupt().
В самом потоке, который возможно будет прерван – нужно устанавливать проверки isInterrupted() во всех ключевых точках (где это необходимо) и обрабатывать соответственно.
Вообще в java doc не рекомендуют пользоваться deprecated методами (такими как Thread.stop()). Метод Thread.stop() убивает поток не обрабатывая и самое главное –
поток может быть «убит» во время выполнения операции, обрыв которой на полуслове оставит некоторый объект в неправильном состоянии, что приведет к появлению трудноотлавливаемой и случайным образом возникающей ошибке
ссылка 1
ссылка 2

Что такое ForkJoinPool в Java?
Благодаря ForkJoinPool можно в небольшом количестве потоков выполнить существенно большее число задач. Это достигается путём так называемого work-stealing’а, когда спящая задача на самом деле не спит, а выполняет другие задачи. Можно выделить две фазы работы с ForkJoin. Сначала форкаем подзадачи, а затем их джойним. Между этими двумя фазами и происходит работа над выполнением всех подзадач.
ссылка 1

Что такое атомарные операции и может ли volatile обеспечить атомарность?
For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64-bit value from one write, and the second 32 bits from another write.
Writes and reads of volatile long and double values are always atomic.
Writes to and reads of references are always atomic, regardless of whether they are implemented as 32-bit or 64-bit values.
ссылка 1
HOLLYWAR DETECTED:
ссылка 2

++long атомарна или нет?
В Java атомарными являются операции чтения/записи всех примитивных типов данных за исключением типов long и double, поскольку эти типы данных занимают два машинных слова, и операции чтения/записи являются составными операциями из двух атомарных операций над старшими и младшими битами числа соответственно.
ссылка 1
ссылка 2

Что такое семафор?
Семафоры представляют еще одно средство синхронизации для доступа к ресурсу. В Java семафоры представлены классом Semaphore из пакета java.util.concurrent.
Для управления доступом к ресурсу семафор использует счетчик, представляющий количество разрешений. Если значение счетчика больше нуля, то поток получает доступ к ресурсу, при этом счетчик уменьшается на единицу. После окончания работы с ресурсом поток освобождает семафор, и счетчик увеличивается на единицу. Если же счетчик равен нулю, то поток блокируется и ждет, пока не получит разрешение от семафора.
ссылка 1

Что такое Long pooling?
Выглядит это примерно следующим образом:
1) Клиент отсылает на сервер обычный ajax-запрос
2) Сервер, вместо того, чтобы быстро обработать этот запрос и отправить ответ клиенту, запускает цикл, в каждой итерации которого следит за возникновением событий (другой клиент добавил запись или удалил).
3) При возникновении события сервер генерирует ответ и отсылает его клиенту, таким образом завершая запрос.
4) Клиент, получив ответ от сервера, запускает обработчик события и параллельно отправляет очередной «длинный» запрос серверу.
ссылка 1
ссылка 2

Метод транзакции в спринге:
При декларативном способе описания транзакционного метода в спринге используется аспектная аннотация.
При программном способе описания – используется особый бин который отвечает за транзакцию.
ссылка 1
ссылка 2
ссылка 3

В чем практический смысл Spring aliasing?
Два бина с одним именем в spring создать нельзя. Однако можно перекрыть первый бин вторым при помощи alias. К примеру, у тебя есть две базы данных. Одна тестовая, а другая промышленная. Тебе не хочется каждый раз при тесте менять название полей в бинах, которые используют базу. Тут на помощь и приходит alias. Плюс при переименованиях бинов alias используется для обратной совместимости. Вещь редко используется, но всё же используется.
ссылка 1

Отличия свойств margin и padding
Создавать промежутки между элементами можно и тем, и другим способом, но если padding – это отступ от содержимого до края блока, то margin – это расстояние от одного блока до другого, межблоковое пространство.
ссылка 1

Расскажите про уровни изолированности в базах данных:
-Read uncommitted (чтение незафиксированных данных)
-Read committed (чтение фиксированных данных)
-Repeatable read (повторяемость чтения)
-Serializable (упорядочиваемость)
ссылка 1
ссылка 2
ссылка 3

Как устроена лямбда внутри?
В Oracle JRE 8 metafactory динамически генерирует Java класс, используя ObjectWeb Asm, который и создает класс-реализацию функционального интерфейса. К созданному классу могут быть добавлены дополнительные поля, если лямбда-выражение захватывает внешние переменные. Этот похоже на анонимные классы Java, но есть следующие отличия:

  • Анонимный класс генерируется компилятором Java.
  • Класс для реализации лямбда-выражения создается JVM во время выполнения.

ссылка 1

Что находится в стеке?
В стеке хранится контекст исполняемых функций, а именно их локальные переменные, переданные в них аргументы, а также адрес возврата и возвращаемое значение. В зависимости от того какой тип имеют эти переменные (ссылочный или примитивный) в стеке могут лежать либо сами значения, либо адрес на место в куче.
ссылка 1

Почему пароль предпочтительней хранить в массиве символов а не строке?
ссылка 1

Включаем спринговые логи

Spring как и любой другой сложный фреймворк умеет сам себя логировать. Иногда нужно уметь смотреть что там внутри происходит. Поэтому давайте попробуем разобраться, как можно заставить его говорить больше, чем он умеет по умолчанию.
Поставляется Spring со встроенным логгером JCL. По сути, это ранний аналог sf4j. Не сам логгер, а обёртка над различными имплементациями. Отсюда наша задача сводится к настройке JCL и стандартного Java логгера. Вполне, думаю, возможно подключить и другие реализации, но это всё зависит от конкретной задачи. Наша же заключается в том, чтобы Spring куда нибудь вывел дебажную информацию о себе. Поэтому Java логгера будет достаточно.
Итак. Задача сводится к следующим трём пунктам:
1) Нам нужно сказать JCL о том, чтобы он подключил Java логгер
2) Настроить Java логгер
3) Получить логи
Давайте теперь постараемся всё это реализовать:
1) Реализуется очень просто. Нужно всего лишь положить файл commons-logging.properties в корень нашего класспаса. Содержимое его должно быть:

Но, если копнуть глубже, и посмотреть внутрь JCL, то мы увидим, что это лишне, поскольку он сам по умолчанию использует Java логгер.
2) Создадим теперь файл для настроек Java логгера. Положите его в любое место и добавьте следующее содержимое:

Обратите внимание на строку 7. С её помощью можно получать логи из того пакета, который именно мы захотим.
3) Теперь чтобы это всё заработало нужно Java логгеру сказать, чтобы он настроился из того файла, который мы только что создали. Это можно сделать через системную переменную:

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

Вот вроде и всё. С таким подходом можно получить логи любого фреймворка, который юзает JCL по умолчанию.

Полезная ссылка

Операторы Inner Join и Outer (left, right, full) Join в SQL (Oracle)

Ключевое слово join в SQL используется при построении select выражений. Инструкция Join позволяет объединить колонки из нескольких таблиц в одну. Объединение происходит временное и целостность таблиц не нарушается. Существует три типа join-выражений:

  • inner join;
  • outer join;
  • cross join;

В свою очередь, outer join может быть left, right и full (слово outer обычно опускается).

В качестве примера (DBMS Oracle) создадим две простые таблицы и сконструируем для них SQL-выражения с использованием join.

В первой таблице будет хранится ID пользователя и его nick-name, а во второй – ID ресурса, имя ресурса и ID пользователя, который может этот ресурс администрировать.

Содержимое таблиц пусть будет таким:

Конструкция join выглядит так:

Где join_type – тип join-выражения, table_name – имя таблицы, которая присоединяется к результату, condition – условие объединения таблиц.

Кострукция join располагается сразу после select-выражения. Можно использовать несколько таких конструкций подряд для объединения соответствующего кол-ва таблиц. Логичнее всего использовать join в том случае, когда таблица имеет внешний ключ (foreign key).

Inner join необходим для получения только тех строк, для которых существует соответствие записей главной таблицы и присоединяемой. Иными словами условие condition должно выполняться всегда. Пример:

Результат будет таким:

В случае с left join из главной таблицы будут выбраны все записи, даже если в присоединяемой таблице нет совпадений, то есть условие condition не учитывает присоединяемую (правую) таблицу. Пример:

Результат выполнения запроса:

Результат показывает все ресурсы и их администраторов, вне зависимотсти от того есть они или нет.

Right join отображает все строки удовлетворяющие правой части условия condition, даже если они не имеют соответствия в главной (левой) таблице:

А результат будет следующим:

Результирующая таблица показывает ресурсы и их администраторов. Если адмнистратор не задействован, эта запись тоже будет отображена. Такое может случиться, например, если ресурс был удален.

Full outer join (ключевое слово outer можно опустить) необходим для отображения всех возможных комбинаций строк из нескольких таблиц. Иными словами, это объединение результатов left и right join.

А результат будет таким:

Некоторые СУБД не поддерживают такую функциональность (например, MySQL), в таких случаях обычно используют объединение двух запросов:

Наконец, cross join. Этот тип join еще называют декартовым произведением (на английском – cartesian product). Настоятельно рекомендую использовать его с умом, так как время выполнения запроса с увеличением числа таблиц и строк в них растет нелинейно. Вот пример запроса, который аналогичен cross join:

Конструкция Join (в сочетании с другими SQL конструкциями, например, group by) часто встречается при программировании под базы данных. Думаю, эта статья будет вам полезна.

Кстати, для проверки своих знаний в области баз данных (и в частности Oracle) рекомендую воспользоваться этим сайтом онлайн тестирования – Тесты по базам данных.

Источник

Использование ThreadLocal переменных

Введение

Вы уже наверно знаете, что поля классов в java бывают статические и не статические. Любое поле класса без модификатора static принадлежит объекту данного класса и создается каждый раз когда создается новый экземпляр класса. Статические переменные(помеченные модификатором static) не принадлежат экземпляру класса и существует всегда в единственном экземпляре независимо от того, сколько экземпляров класса было создано. Появившийся в java 1.2 класс java.lang.ThreadLocal по сути предоставляет нам ещё одну область жизни объектов, ThreadLocal предоставляет абстракцию над переменными локальными по отношению к потоку испольнения java.lang.Thread. ThreadLocal переменные отличаются от обычных переменных тем, что у каждого потока свой собственный, индивидуально инициализируемый экземпляр переменной, доступ к которой он получает через методы get() или set().

Я в своей практике встречался с четырьмя основными целями применения ThreadLocal переменных:
1. Упрощение API.
2. Cинтаксический сахар.
3. Кеширование непотокобезопасных(non thread safe) ресурсов.
4. Уменьшение области конкуренции между потоками(lock striping).

Упрощение API с помощью ThreadLocal.

Допустим Вы разрабатываете JEE веб приложение, после прохождения аутентификации на странице логина информация о пользователе запоминается в http сессии, и Вам в любой точке кода может понадобится информация о пользователе от которого пришел http запрос.
Наверняка Вы не захотите всю логику помещать в сервлеты и JSP, Вы выделети в приложение несколько слоев(бизнесс логика, доступ к данным и.т.д.), но после распределния ответсвенности по слоям у Вас может возникнуть проблема с тем, что не в каждой точке кода будет доступ к Http сессии, соответсвенно не везде можно будет узнать от какого пользователя пришел запрос.
Встает вопрос? а как проектировать свой API? Добавлять в каждый метод каждого класса дополнительный параметр представляющий данные пользователе?

До появления ThreadLocal это был единственный выход, с появлением же ThreadLocal мы можем привязать данные о пользователи к потоку обработки http запроса, и достать эту информацию в любом месте программы. Для этого нам понадобится зарегистрировать слушателя в контексте веб приложения который будет срабатывать на любой входящий http запрос:

Итак начнем с класса для реализации потокобезопсного контеста пользователя:

Как мы знаем сервера приложений могут выполнять тысячи запросов одновременно и на первый взгляд такой код не потоко безопасен. Ведь действительно мы запоминаем текущего пользователя в статической переменной, а статическая переменная одна на весь класс, и вроде бы как паралельные http запросы должны перетирать данные друг друга.
Но вся фишка ThreadLocal заключается в том что имея всего одну ThreadLocal переменную, мы можем иметь различное значение для каждого из потоков, то есть один поток никогда не прочтет, удалит или не перезатрет данные присвоенные другим потоком. Таким образом несмотря на разделяемую статическую переменную код выше потоко-безопасен.Хранилище аутентификацционных данных написано, теперь нужно написать и сконфигурировать в web.xml слушателя входящих HTTP запросов, который бы при поступлении запроса присоеденяя данные о пользователе к потоку обработки а по завершении обработки запроса, очищал бы эту информацию.

Дело осталось за малым воспользоваться написаными функционалом из любой точки приложения в которой нет доста к http запросу или сессии:

Конечно же функционал реализованный выше редко когда придется писать самому, есть множество библиотек связанных с security в которых это уже реализовано, например spring-security, стоит только иметь в виду что все они будут точно также работать посредством ThreadLocal. Построение API вокруг ThreadLocal широко используется в java enterprise edition и используется не только для ассоциирования контекста безопасности с потоком, но и для других вещей как например транзакции, открытые JPA сессии. Так же хочу заметить что использование ThreadLocal в JEE окружении сопряжено с возникновением многих проблемам и без глубокого понимания платформы jee, многопоточности и механизма загрузки классов от использования ThreadLocal в JEE лучше отказаться. Проблемы порождаемые ThreadLocal переменными в JEE окружении описаны в конце статьи.

2. Cинтаксический сахар или программирование на языке.

ThreadLocal можно использовть для добавления синтаксического сахара в язык java и многие библиотеки этим пользуются, например mybatis:

Код получился легко читаемым, как видно функции BEGIN, SELECT и.т.д. не вызываются ни на одном объекте, то есть они статические и за счет статического импорта появившегося в java 5, вызов таких функций можно осуществлять без префикса класса. Потоко-безопасность достигается за счет того что каждый поток выполнения имеет собственный экземпляр билдера запросов. Конечно следует ожидать, что c появлением лямбд в java 8, использование ThreadLocal в качестве синтаксического сахара потеряет свою актуальность

Кеширование непотокобезопасных(non thread safe) ресурсов.

Однажды делая код ревью одного класса я обнаружил очень интересный баг многопоточности:

Класс использовался как кастомный адаптер для даты в JAXB. Вроде бы простой маленьки класс и в нём негде ошибится, однако есть одно но, класс java.text.SimpleDateFormat не является потоко безопасным, параллельные потоки должны либо синхронизировать доступ к инстансу объекта данного класса, либо отказаться от разделения одного инстанса SimpleDateFormat.
То есть просто создать один экземпляр формата и запомнить в статической переменной нельзя, иначе мы получим мусор на выходе если форматировать даты паралельно из нескольких потоков. Честно говоря в приложении рассчитаном на входящий поток данных 5 тысяч входящих документов в секунду, ни генерировать мусор создавая каждый раз новый экземпляр формата, ни тем более создавать бутылочное горлышко в виде synchronized блоков мне не хотелось, и поскольку стояло жесткое требование по максимуму отказаться от библиотек не входящих в j2se, то есть нельзя было использовать сторонние реализации форматеров то код выше превратился в следующее:

Как видно обеспечено кеширование объектов DateFormat без синхронизации. В статье Java Best Practices – DateFormat in a Multithreading Environmentприведены результаты бенчмарков показывающих что такой подход позволяет увеличить производительность парсинга дат до 8 раз по сравнению с созданием каждый раз нового экземпляра формата.

Сужение области конкуренции между потоками(lock striping).

Lock striping техника представления сложного объекта, к которому осуществляется конкуретный доступ в виде отдельных маленьких частей, каждую часть такого объекта можно менять без блокировки целого объекта. Например техника lock striping применена в CuncurrentHashMap – вся коллекция разбита на регионы, и треды при модификации не конкурируют за всю коллекцию целиком, они конкурируют за её отдельные регионы, таким образом острота конкуренции снижается.

Прежде всего для этого параграфа хотелось бы сразу поместить disclaimer и cсылку на эту презентацию
java8 новинки в java.util.concurrent, в презентации авторитетные специалисты в области java оптимизации в квалификации которых не возникает ни каких сомнений, крайне не рекомендуют использовать ThreadLocal для сuncurrent оптимизаций.

Однако пока java8 ещё не зарелизена, а в продакшн энтерпрайз приложений java8 попадет вообще не скоро, то пример использования ThreadLocal я всё же опубликую.

И так представим высоконагруженное приложение с тысячами рабочих потоков. Потоки занимаются тем что обрабатывают пачки входящих документов и нам бы хотелось собирать некоторую статистику о работе приложения, а конкретно количество обработанных документов. Но при этом мы бы хотели чтобы сбор статистики обходился нам бесплатно, и не вносил бы лишних нагрузку в приложение.
Какие сть варианты решения:
Выполнять запрос SELECT COUNT(*) FROM TABLE в базу данных – на таких объемах не очень удачное решение.
Заводить AtomicLong – поможет на небольшом количестве потоков, но не на тысячах.

Итак применим ThreadLocal. Общее количество обработанных сервером документов можно представить как сумму обработанных документов каждым рабочим потоком. То есть к каждому треду можно поставить соответсвии число, и он будет увеличивать это число вообще без конкуренции с каким либо другим потоком, то есть как мы и хотели сбор статистики обходится нам бесплатно.

Для сокращения кода и упрощения понимания концепции ThreadLocal, из кода удалена обработка ситуаций внезапной смерти потоков. По хорошему нужно обрабатывать ситуацию, когда ThreadLocal собирается сборщиком мусора потому, что поток которому она принадлежит более недостижим из корня, но чтобы не отвлекать внимание читателя в сторону PhantomReference обработка этих ситуаций из кода удалена, а аспекты взаимодействия ThreadLocal и сборки мусора вынесены в отдельный параграф.А вот небольшой тест показывающий применение такого аккомулятора: