Потоки
- Поток — это небольшая исполняемая программа. В процессе может быть задействовано несколько потоков (облегченный процесс).
Способы работы с потоками:
1. Расширение класса Thread.
2. Реализация интерфейса Runnable.
public class Test extends Thread { public void run(){ // logic } Thread t1 = new Test(); // Thread t1 = new Thread(new Test()); FOR RUNNABLE t1.Start(); }
- Лучше использовать Runnable, т.к. в нем заложена цепь множественного наследования.
- Метод Start() вызывает run(). Но вызвать run() напрямую нельзя — этот метод не создает нового потока. Run() заставляет текущий поток выполнять тот же run(). Получается вызов прямого метода без потока.
Object.wait() → Блокировка потока до вызова уведомления.
Object.notify() → Пробуждает поток, ожидающий на мониторе указанного объекта, и переводит его в «работоспособное» состояние (Runnable mode).
Object.notifyAll() → Пробуждает все потоки, ожидающие на мониторе данного объекта.
Thread.yield() → Переводит текущий поток в «работоспособное» состояние и переходит к следующему потоку.
Thread.sleep() → На несколько миллисекунд переводит поток в спящее/приостановленное состояние. Время в методе sleep передается как параметр.
- Поток-демон (Daemon) — это поток с низким приоритетом. Всегда выполняется в фоновом режиме (например, поток Garbage Collection).
- Во избежание взаимной блокировки не синхронизируйте код с блокирующим вызовом.
Мьютекс и семафор
- Оба используются для синхронизации потока. Семафор — это простой тип ограничивающих объектов. Для каждого семафора задаются начальное и максимальное значение. Блокировка выполняет взаимное исключение (мьютекс), но не работает с запросами и не решает проблем с очередностью (например, очередность печати или шаблон «Производитель/Потребитель»). Для этих целей есть семафор.
public class Semaphore { int value; // No of users that can use Resource public Semaphore(int init) { if (init < 0) { init = 0; } value = init; } // Acquiring Resource public synchronized void down() { while (value == 0) { try { wait(); } catch (InterruptedException e) { } value — ; } } // Releasing Resource public synchronized void up() { value++; notify(); } }
- Класс Semaphore инициализируется перед методом Start(). Блокировка делается через вызов semaphoreObject.down(); для завершения задачи вызывается semaphoreObject.up().
- Мьютекс — это ни что иное, как «Семафор взаимного исключения». Он ссылается на тип блокируемого объекта, который может одновременно принадлежать лишь одному потоку. Только поток, осуществляющий захват блокировки, может снять эту блокировку в мьютексе. Когда мьютекс заблокирован, все попытки захвата блокировки приведут к ошибке или блокировке действий, даже если они выполняются тем же самым потоком.
Обработка ошибок
- Ошибка — это нарушение динамических связей или ручной/машинный сбой, который не должен происходить при нормальных обстоятельствах.
- Непроверяемые исключения — это исключения в среде выполнения исполняющей программы JVM (к примеру, nullPointerException).
public class MyException extends Exception { private String errorCode = “Unkown_Exception”; public MyException(String message, String errorCode){ super(message); this.errorCode = errorCode; } Public String getErrorCode(){ Return this.errorCode; } }
- Ключевое слово throw выбрасывает исключения в среду выполнения для его дальнейшей обработки.
- Если нужен выброс исключения без обработки, то в сигнатуру метода добавляется ключевое слово throws — так вызывающая программа находит исключения, которые выбрасываются методом.
Класс Observable и интерфейс Observer
- Существует шаблон проектирования под названием «Наблюдатель». Довольно часто необходимо уведомить некие объекты (Observer) о происходящем в конкретном экземпляре (Observable).
- Обычно этот шаблон используют в Ajax, чтобы уведомить несколько объектов при возникновении события (onclick по ссылке).
Как это делается:
1. Создается класс наблюдаемых — Observable.
2. Добавляется несколько классов наблюдателей — Observer.
3. Регистрируются классы Observer в объекте Observable (addObserver).
4. Вызывается notifyObserver для уведомления всех наблюдателей о произошедшем событии.
JDBC
- JDBC — это Java API (набор классов и интерфейсов), используемый для подключения и исполнения запросов в базе данных. Подключение базы данных делается через драйвер JDBC.
- JDBC Driver — это программный компонент, позволяющий Java-приложению взаимодействовать с базой данных.
Существуют 3 типа запросов в JDBC:
- Statement: повторяющаяся компиляция запроса.
2. PreparedStatement: однократная компиляция запроса. Производительность здесь в разы выше.
3. CallableStatement: исполняет процедуры и функции.
- Класс DriverManager управляет зарегистрированными драйверами.
- Объект ResultSet — это строка таблицы.
- Интерфейс ResultSetMetaData возвращает даннные о таблице: общее количество колонок, их тип, название и т.д.
Пример кода:
@Resource(mappedName = “jdbc/DarmAircom”) private javax.sql.DataSource dataSource; Connection connection = dataSource.getConnection(); CallableStatement statement = connection.prepareCall(“call ABC.ABC_PKG.PROCEDURE_NAME(?,?,?,?,?,?,?,?,?,?,?,?,?)”); statement.execute(); statement.close(); connection.close();
Чтение и запись файла
ByteStream — для чтения и записи бинарных данных.
CharacterStreams работает с символами, а не байтами.
FileInputStream содержит входной байт из файла и реализует входной стрим.
FileOutputStream: записывает данные в файл и реализует выходной стрим.
Чтение из файла: Общий способ чтения символов из файла — через класс FileReader. Класс BufferedReader можно обернуть вокруг любого Reader (например, FileReader) для буферизации входных данных и увеличения эффективности.
public static void readFromFile(String fileName) throws IOException { int total = 0; BufferedReader in = new BufferedReader( new FileReader(fileName)); for ( String s = in.readLine(); s != null; s = in.readLine() ) { // GOT EACH LINE } in.close(); }
Пример названия файла: “C:/Users/chellapilla_m/Desktop/task.txt”
Запись в файл: Ниже приведен пример записи в файл через FileOutputStream.
File fout = new File(file_location_string); FileOutputStream fos = new FileOutputStream(fout); BufferedWriter out = new BufferedWriter(new OutputStreamWriter(fos)); out.write(“something”); Using FileWriter: FileWriter fstream = new FileWriter(file_location_string); BufferedWriter out = new BufferedWriter(fstream); out.write(“something”);
FileOutputStream нужен для записи стримов сырых байтов (raw byte). К примеру, информация об изображении. Для записи стримов из символов лучше использовать FileWriter.
Шаблоны проектирования
Шаблон «Одиночка»
- В JVM создается по одному экземпляру класса на каждый загрузчик классов.
- Конструктор имеет приватный модификатор доступа, поэтому нельзя создавать экземпляры вне класса.
- Единственный метод создания экземпляров — через getInstance() с публичным модификатором доступа. Это статичный метод.
- Примеры одиночных классов: SessionFactory и Logger.
public class Singleton { private static Singleton instance = new Singleton(); private singleton() {} public static Singleton getInstance(){ return instance; } }
Шаблон «Фабрика»
Это самый популярный шаблон проектирования в Java. Здесь мы создаем объект без демонстрации логики создания клиенту и обращаемся к недавно созданным объектам через общий интерфейс. Пример:
interface Dog { public void speak (); } class Poodle implements Dog { public void speak() { System.out.println(“The poodle says \”arf\””); } } class Rottweiler implements Dog { public void speak() { System.out.println(“The Rottweiler says (in a very deep voice) \”WOOF!\””); } } class DogFactory { public static Dog getDog(String criteria) { if ( criteria.equals(“small”) ) return new Poodle(); else if ( criteria.equals(“big”) ) return new Rottweiler(); return null; } } public class JavaFactoryPatternExample { public static void main(String[] args) { Dog dog = DogFactory.getDog(“small”); dog.speak(); dog = DogFactory.getDog(“big”); dog.speak(); } }
Шаблон «Адаптер»
Обеспечивает совместную работу двух несвязанных интерфейсов. Пример:
public interface SocketAdapter { public Volt get120Volt(); public Volt get12Volt(); public Volt get3Volt(); }
Шаблон «Строитель»
Это ответвление шаблона «Фабрика». Класс Builder создает сложный объект в несколько этапов.
Важные моменты
- java.util.Date отображает время и дату, а java.sql.Date показывает только дату (java.sql.Time является дополнением java.sql.Date; показывает только время и расширяет java.util.Date)
- Синхронизация — это способность контролировать доступ нескольких потоков к общим ресурсам.
- Статичные синхронизированные методы блокируют класс. Поэтому, как только поток достигает синхронизированного статичного метода, сам класс блокируется монитором потока, и ни один поток не сможет достичь тех же методов данного класса. Это главное отличие от методов создания экземпляров класса, в которых сразу несколько потоков получают одновременный доступ к одинаковым синхронизированным методам создания экземпляров класса. Пример: public synchronized void synchronizedMethod() {}
- Thread Dump — список всех активных потоков.
- Утечка потоков — приложение выдает ссылки на объекты потока с ошибками, из-за чего некоторые потоки не попадают в сборщик мусора, и количество неиспользуемых потоков растает.
- Пул потоков — это коллекция потоков с разбивкой по задачам. Используется как альтернатива созданию нового потока для каждой задачи.
- Конструкторы не синхронизируются, т.к. другие потоки не видят новых объектов до того, как какой-либо поток не завершит их создания.
- Метод run() и класс с Runnable можно синхронизировать. Если синхронизировать метод run(), то перед выполнением этого метода блокировка на объекте Runnable будет занята другим процессом.
- Кластер — это группа компьютеров, которые обособлено запускают приложение. Кластеризация нужна для достижения максимальной доступности серверных приложений. Основная цель — 100% доступность или нулевое время простоя.
- Балансировщик нагрузки — простая технология для распределения рабочей нагрузки по нескольким кластерам или машинам.
- Переключение при отказе — переход на другую машину в случае неисправности первой.
- JEE приложения основаны на концепции распределенных веб-приложений; обеспечивают переключение сеансов и балансировку нагрузок. Необходимо добавление опознавательного тега <distributable/> в web.xml file.
- Java — это всегда pass-by-value (передача по значению). Самое трудно здесь — понять, что Java передает объекты как ссылки, и именно эти ссылки передаются по значению.
- Методы Arrays.sort() используют сортировку слияния или настроенную быструю сортировку, в зависимости от типов данных. Они также переключаются на сортировку вставками, если объектов в массиве меньше семи. Таким образом, эффективность реализации повышается. Классы Collections опосредованно используют Arrays.sort.
- 0xDEADBEEF («мертвая говядина») часто используется для обозначения аварийных сбоев в программах и взаимной блокировки встроенных систем. Изначально DEADBEEF показывал выделенные участки памяти без инициализации. При сканировании дампов памяти можно тоже увидеть DEADBEEF. Он выступает в роли «магического числа отладки» (magic debug value) в системах IBM RS/6000, Mac OS, 32-разрядных процессорах PowerPC и в Commodore Amiga. В Solaris от Sun Microsystems DEADBEEF отмечает освобожденную память ядра. OpenVMS с процессорами Alpha можно найти DEAD_BEEF, нажав комбинацию CTRL-T. В консоли DEC Alpha SRM есть специальный фоновый процесс для «отлова» ошибок памяти. Они идентифицируются компьютером как «BeefEater waiting on 0xdeadbeef».
Вопрос: Зачем нужна функция main()?
Выполнение программы начинается с метода main().
Вопрос: Почему метод main() публичный?
Так метод доступен для JVM — она начинает выполнение программы вне класса.
Вопрос: Почему main() статичный?
Для его исполнения не нужен объект.
Вопрос: Почему String args[]?
Параметры — это аргументы командной строки. Просто так вышло, что разработчик Java счел их обязательными. Никакой особой нагрузки они не несут.
Вопрос: Для чего нужен out в System.out.println()?
«out» — это объект класса PrintStream и статический член класса System, который вызывает функцию println().
Вопрос: Как в Java реализован Random?
Класс java.util.Random реализует линейный конгруэнтный метод.
Его формула:
number(i+1) = (a * number(i) + c) mod m
где m, c и a — константы, а i — выбранное начальное значение.
Вопрос: Что такое regex в Java?
Регулярные выражения (regex) определяют поисковый образ строк.
private static final String EMAIL_PATTERN = “^[_A-Za-z0–9-\\+]+(\\.[_A-Za-z0–9-]+)*@” + “[A-Za-z0–9-]+(\\.[A-Za-z0–9]+)*(\\.[A-Za-z]{2,})$”; Pattern pattern = Pattern.compile(EMAIL_PATTERN); Matcher matcher = pattern.matcher(inputString); boolean result = matcher.matches();
- Ключевое слово assert используется для утверждений, т.е. выражений, которые, по мнению программиста, всегда истинны в данном участке программы. Assert помогает в тестировании и отладке.
Вопрос: Чем отличаются вложенные и внутренние классы?
Внутренними называются не статичные вложенные классы.
Вопрос: Что такое вложенный интерфейс?
Это любой интерфейс, объявленный в классе или интерфейсе. Всегда статичный по умолчанию.
Перевод статьи Madhu Pathy: A Beginner’s Guide to Java: Part 3 of 4