завершение потока Second




завершение потока First

завершение main

Несмотря на вызов метода join() для потока tr1, поток tr2 будет работать, в отличие от потока main, который сможет продолжить свое выполнение только по завершении потока tr1.

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

// пример # 5: задержка потока: YieldRunner.java

package chapt14;

public class YieldRunner {

public static void main(String[] args) {

new Thread() {

public void run() {

System. out. println("старт потока 1");

Thread. yield ();

System. out. println("завершение 1");

}

}.start();

new Thread() {

public void run() {

System. out. println("старт потока 2");

System. out. println("завершение 2");

}

}.start();

}

}

В результате может быть выведено:

старт потока 1

старт потока 2

завершение 2

завершение 1

Активизация метода yield() в коде метода run() первого объекта потока приведет к тому, что, скорее всего, первый поток будет остановлен на некоторый квант времени, что даст возможность другому потоку запуститься и выполнить свой код.

Потоки-демоны

Потоки-демоны работают в фоновом режиме вместе с программой, но не являются неотъемлемой частью программы. Если какой-либо процесс может выполняться на фоне работы основных потоков выполнения и его деятельность заключается в обслуживании основных потоков приложения, то такой процесс может быть запущен как поток-демон. С помощью метода setDaemon(boolean value), вызванного вновь созданным потоком до его запуска, можно определить поток-демон. Метод boolean isDaemon() позволяет определить, является ли указанный поток демоном или нет.

/* пример # 6: запуск и выполнение потока-демона: DemoDaemonThread.java */

package chapt14;

class T extends Thread {

public void run() {

try {

if (isDaemon()){

System. out. println("старт потока-демона");

sleep (10000); // заменить параметр на 1

} else {

System. out. println("старт обычного потока");

}

} catch (InterruptedException e) {

System. err. print("Error" + e);

} finally {

if (!isDaemon())

System. out. println(

"завершение обычного потока");

Else

System. out. println(

"завершение потока-демона");

}

}

}

package chapt14;

 

public class DemoDaemonThread {

public static void main(String[] args) {

T usual = new T();

T daemon = new T();

daemon.setDaemon(true);

daemon.start();

usual.start();

System. out. println(

"последний оператор main");

}

}

В результате компиляции и запуска, возможно, будет выведено:

последний оператор main

старт потока-демона

старт обычного потока

завершение обычного потока

Поток-демон (из-за вызова метода sleep(10000)) не успел завершить выполнение своего кода до завершения основного потока приложения, связанного с методом main(). Базовое свойство потоков-демонов заключается в возможности основного потока приложения завершить выполнение потока-демона (в отличие от обычных потоков) с окончанием кода метода main(), не обращая внимания на то, что поток-демон еще работает. Если уменьшать время задержки потока-демона, то он может успеть завершить свое выполнение до окончания работы основного потока.

Потоки в графических приложениях

Добавить анимацию в апплет можно при использовании потоков. Поток, ассоциированный с апплетом, следует запускать тогда, когда апплет становится видимым, и останавливать при сворачивании браузера. В этом случае метод
repaint() обновляет экран, в то время как программа выполняется. Поток создает анимационный эффект повторением вызова метода paint() и отображением графики в новой позиции.

/* пример # 7: освобождение ресурсов апплетом: GraphicThreadsDemo.java */

package chapt14;

import java.awt.Color;

import java.awt.Container;

import java.awt.Graphics;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JPanel;

 

public class GraphicThreadsDemo extends JFrame {

JPanel panel = new JPanel();

Graphics g;

JButton btn = new JButton("Добавить шарик");

int i;

 

public GraphicThreadsDemo() {

setBounds(100, 200, 270, 350);

Container contentPane = getContentPane();

contentPane.setLayout(null);

btn.setBounds(50, 10, 160, 20);

contentPane.add(btn);

panel.setBounds(30, 40, 200, 200);

panel.setBackground(Color.WHITE);

contentPane.add(panel);

btn.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent ev) {

new BallThread(panel).start();

i++;

repaint();

}

});

}

public static void main(String[] args) {

GraphicThreadsDemo frame =

new GraphicThreadsDemo();

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.setVisible(true);

}

public void paint(Graphics g){

super. paint(g);

g.drawString("Количество шариков: " + i, 65, 300);

}

}

class BallThread extends Thread {

JPanel panel;

private int posX, posY;

private final int BALL_SIZE = 10;

private double alpha;

private int SPEED = 4;

 

BallThread(JPanel p) {

this. panel = p;

//задание начальной позиции и направления шарика

posX = (int)((panel.getWidth() - BALL_SIZE)

* Math.random());

posY = (int)((panel.getHeight() - BALL_SIZE)

* Math.random());

alpha = Math.random() * 10;

}

public void run() {

while (true) {

posX += (int)(SPEED * Math.cos(alpha));

posY += (int)(SPEED * Math.sin(alpha));

//вычисление угла отражения

if (posX >= panel.getWidth() - BALL_SIZE)

alpha = alpha + Math.PI - 2 * alpha;

else if (posX <= 0)

alpha = Math.PI - alpha;

if (posY >= panel.getHeight() - BALL_SIZE)

alpha = -alpha;

else if (posY <= 0)

alpha = -alpha;

paint(panel.getGraphics());

}

}

public void paint(Graphics g) {

//прорисовка шарика

g.setColor(Color.BLACK);

g.fillArc(posX, posY, BALL_SIZE, BALL_SIZE, 0, 360);

g.setColor(Color.WHITE);

g.drawArc(posX + 1, posY + 1, BALL_SIZE,

BALL_SIZE, 120, 30);

try {

sleep(30);

} catch (InterruptedException e) {

e.printStackTrace();

}

//удаление шарика

g.setColor(panel.getBackground());

g.fillArc(posX, posY, BALL_SIZE, BALL_SIZE, 0, 360);

}

}

Рис.14.2. Потоки в апплетах

При вызове метода stop() апплета поток перестает существовать, так как ссылка на него устанавливается в null и освобождает ресурсы. Для следующего запуска потока необходимо вновь инициализировать ссылку и вызвать метод start() потока.

Методы synchronized

Очень часто возникает ситуация, когда несколько потоков, обращающихся к некоторому общему ресурсу, начинают мешать друг другу; более того, они могут повредить этот общий ресурс. Например, когда два потока записывают информа­цию в файл/объект/поток. Для предотвращения такой ситуации может использоваться ключевое слово synchronized. Синхронизации не требуют только атомарные процессы по записи/чтению, не превышающие по объему 32 бит.

В качестве примера будет рассмотрен процесс записи информации в файл двумя конкурирующими потоками. В методе main() классa SynchroThreads создаются два потока. В этом же методе создается экземпляр класса Synchro, содержащий поле типа FileWriter, связанное с файлом на диске. Экземпляр Synchro передается в качестве параметра обоим потокам. Первый поток записывает строку методом writing() в экземпляр класса Synchro. Второй поток также пытается сделать запись строки в тот же самый объект Synchro. Для избежания одновременной записи такие методы объявляются как synchronized. Синхронизированный метод изолирует объект, после чего объект становится недоступным для других потоков. Изоляция снимается, когда поток полностью выполнит соответствующий метод. Другой способ снятия изоляции – вызов метода wait() из изолированного метода.

В примере продемонстрирован вариант синхронизации файла для защиты от одновременной записи информации в файл двумя различными потоками.

/* пример # 8: синхронизация записи информации в файл: MyThread.java: Synchro.java: SynchroThreads.java */

package chapt14;

import java.io.*;

 

public class Synchro {

private FileWriter fileWriter;

 

public Synchro(String file) throws IOException {

fileWriter = new FileWriter(file, true);

}

public void close() {

try {

fileWriter.close();

} catch (IOException e) {

e.printStackTrace();

}

}

public synchronized void writing(String str, int i) {

try {

System. out. print(str + i);

fileWriter.append(str + i);

Thread. sleep ((long)(Math. random () * 50));

System. out. print("->" + i + " ");

fileWriter.append("->" + i + " ");

} catch (IOException e) {

System. err. print("ошибка файла");

e.printStackTrace();

} catch (InterruptedException e) {

System. err. print("ошибка потока");

e.printStackTrace();

}

}

}

package chapt14;

 

public class MyThread extends Thread {

private Synchro s;

 

public MyThread(String str, Synchro s) {

super (str);

this. s = s;

}

public void run() {

for (int i = 0; i < 5; i++) {

s.writing(getName(), i);

}

}

}

package chapt14;

import java.io.*;

 

public class SynchroThreads {

public static void main(String[] args) {

try {

Synchro s = new Synchro("c:\\temp\\data.txt");

 

MyThread t1 = new MyThread("First", s);

MyThread t2 = new MyThread("Second", s);

t1.start();

t2.start();

t1.join();

t2.join();

s.close();

} catch (IOException e) {

System. err. print("ошибка файла");

e.printStackTrace();

} catch (InterruptedException e) {

System. err. print("ошибка потока");

e.printStackTrace();

}

}

}

В результате в файл будет выведено:

First0->0 Second0->0 First1->1 Second1->1 First2->2

Second2->2 First3->3 Second3->3 First4->4 Second4->4

Код построен таким образом, что при отключении синхронизации метода writing() при его вызове одним потоком другой поток может вклиниться и произвести запись своей информации, несмотря на то, что метод не завершил запись, инициированную первым потоком.

Вывод в этом случае может быть, например, следующим:

First0Second0->0 Second1->0 First1->1 First2->1 Second2->2 First3->3 First4->2 Second3->3 Second4->4 ->4

Инструкция synchronized

Синхронизировать объект можно не только при помощи методов с соответству-
ющим модификатором, но и при помощи синхронизированного блока кода. В этом случае происходит блокировка объекта, указанного в инструкции synchronized,
и он становится недоступным для других синхронизированных методов и блоков. Обычные методы на синхронизацию внимания не обращают, поэтому ответственность за грамотную блокировку объектов ложится на программиста.

/* пример # 9: блокировка объекта потоком: TwoThread.java */

package chapt14;

public class TwoThread {

public static void main(String args[]) {

final StringBuffer s = new StringBuffer();

new Thread() {

public void run() {

int i = 0;

synchronized (s) {

while (i++ < 3) {

s.append("A");

try {

sleep (100);

} catch (InterruptedException e) {

System. err. print(e);

}

System. out. println(s);

}

} //конец synchronized

}

}.start();

new Thread() {

public void run() {

int j = 0;

synchronized (s) {

while (j++ < 3) {

s.append("B");

System. out. println(s);

}

} //конец synchronized

}

}.start();

}

}

В результате компиляции и запуска будет, скорее всего (так как и второй поток может заблокировать объект первым), выведено:

A

AA

AAA

AAAB

AAABB

AAABBB

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

Если в коде убрать синхронизацию объекта s, то вывод будет другим, так как другой поток сможет получить доступ к объекту и изменить его раньше, чем первый закончит выполнение цикла.

В следующем примере рассмотрено взаимодействие методов wait() и
notify() при освобождении и возврате блокировки в synchronized блоке. Эти методы используются для управления потоками в ситуации, когда необходимо задать определенную последовательность действий без повторного запуска потоков.

Метод wait(), вызванный внутри синхронизированного блока или метода, останавливает выполнение текущего потока и освобождает от блокировки захваченный объект, в частности объект lock. Возвратить блокировку объекта потоку можно вызовом метода notify() для конкретного потока или
notifyAll() для всех потоков. Вызов может быть осуществлен только из другого потока, заблокировавшего, в свою очередь, указанный объект.

/* пример # 10: взаимодействие wait() и notify(): Blocked.java: Runner.java */

package chapt14;

 

public class Blocked {

private int i = 1000;

 

public int getI() {

return i;

}

public void setI(int i) {

this. i = i;

}

public synchronized void doWait() {

try {

System. out. print("Не ");

this. wait(); /* остановка потока и

освобождение блокировки*/

System. out. print("сущностей "); // после возврата блокировки

Thread. sleep (50);

} catch (InterruptedException e) {

e.printStackTrace();

}

for (int j = 0; j < 5; j++) i/=5;

System. out. print("сверх ");

}

}

package chapt14;

 

public class Runner {

public static void main(String[] args) {

Blocked lock = new Blocked();

new Thread() {

public void run() {

lock.doWait();

}}.start();

try {

Thread. sleep (500);

} catch (InterruptedException e) {

e.printStackTrace();

}

synchronized (lock) { // 1

lock.setI(lock.getI() + 2);

System. out. print("преумножай ");

lock.notify(); // возврат блокировки

}

synchronized (lock) { // 2

lock.setI(lock.getI() + 3);

//блокировка после doWait()

System. out. print("необходимого. ");

try {

lock.wait(500);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

System. out. print("=" + lock.getI());

}

}

В результате компиляции и запуска будет выведено следующее сообщение:

Не преумножай сущностей сверх необходимого. =3

Задержки потоков методом sleep() используются для точной демонстрации последовательности действий, выполняемых потоками. Если же в коде приложения убрать все блоки синхронизации, а также вызовы методов wait() и notify(), то вывод может быть следующим:

Не сущностей преумножай необходимого. =1005сверх

Состояния потока

В классе Thread объявлено внутреннее перечисление State, простейшее применение элементов которого призвано помочь в отслеживании состояний потока в процессе функционирования приложения и, как следствие, в улучшении управления им.

/* пример # 11: состояния NEW, RUNNABLE, TIMED_WAITING, TERMINATED: ThreadTimedWaitingStateTest.java */

package chapt14;

public class ThreadTimedWaitingStateTest extends Thread {

public void run() {

try {

Thread. sleep (50);

} catch (InterruptedException e) {

System. err. print("ошибка потока");

}

}

public static void main(String [] args){

try {

Thread thread = new ThreadTimedWaitingStateTest();

// NEW – поток создан, но ещё не запущен

System. out. println("1: " + thread.getState());

thread.start();

// RUNNABLE – поток запущен

System. out. println("2: " + thread.getState());

Thread.sleep(10);

// TIMED_WAITING

// поток ждет некоторое время окончания работы другого потока

System.out.println("3: " + thread.getState());

thread.join();

// TERMINATED – поток завершил выполнение

System.out.println("4: " + thread.getState());

} catch (InterruptedException e) {

System.err.print("ошибка потока");

}

}

}

В результате компиляции и запуска будет выведено:

NEW

RUNNABLE

TIMED_WAITING

TERMINATED

/* пример # 12: состояния BLOCKED, WAITING: ThreadWaitingStateTest.java */

package chapt14;

public class ThreadWaitingStateTest extends Thread {

public void run() {

try {

synchronized (this) {

wait();

}

} catch (InterruptedException e) {

System.err.print("ошибка потока");

}

}

 

public static void main(String[] args) {

try {

Thread thread = new ThreadWaitingStateTest();

thread.start();

synchronized (thread) {

Thread.sleep(10);

// BLOCKED – because thread attempting to acquire a lock

System.out.println("1: " + thread.getState());

}

Thread.sleep(10);

// WAITING – метод wait() внутри synchronized

// останавил поток и освободил блокировку

System.out.println("2: " + thread.getState());

thread.interrupt();

} catch (InterruptedException e) {

System.err.print("ошибка потока");

}

}

}

В результате компиляции и запуска будет выведено:

BLOCKED

WAITING

Потоки в J2SE 5

Java всегда предлагала широкие возможности для мультипрограммирования: потоки – это основа языка. Однако очень часто использование таких возможностей становилось ловушкой: правильно написать и отладить многопоточную программу достаточно сложно.

В версии 1.5 языка добавлены пакеты классов java.util.concurrent.locks, java.util.concurrent.atomic, java.util.concurrent, возможности которых обеспечивают более высокую производительность, масштабируемость, построение потокобезопасных блоков параллельных (concurrent) классов, вызов утилит синхронизации, использование семафоров, ключей и atomic-переменных.

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

Ограниченно потокобезопасные (thread safe) коллекции и вспомогательные классы управления потоками сосредоточены в пакете java.util.concurrent. Среди них можно отметить:

· параллельные классы очередей ArrayBlockingQueue (FIFO очередь с фиксированой длиной), PriorityBlockingQueue (очередь с приоритетом) и ConcurrentLinkedQueue (FIFO очередь с нефиксированой длиной);

· параллельные аналоги существующих синхронизированых классов-коллекций ConcurrentHashMap (аналог Hashtable) и
CopyOnWriteArrayList (реализация List, оптимизированная для случая, когда количество итераций во много раз превосходит количество вставок и удалений);

· механизм управления заданиями, основанный на возможностях класса Executor, включающий пул потоков и службу их планирования;

· высокопроизводительный класс Lock, поддерживающий ограниченные ожидания снятия блокировки, прерываемые попытки блокировки, очереди блокировки и установку ожидания снятия нескольких блокиро­вок посредством класса Condition;

· классы атомарных переменных (AtomicInteger, AtomicLong,
AtomicReference), а также их высокопроизводительные аналоги SyncronizedInt и др.;

· классы синхронизации общего назначения, такие как Semaphore, CountDownLatch (позволяет потоку ожидать завершения нескольких операций в других потоках), CyclicBarrier (позволяет нескольким потокам ожидать момента, когда они все достигнут какой-либо точки) и Exchanger (позволяет потокам синхронизироваться и обмениваться информацией);

· обработка неотловленных прерываний: класс Thread теперь поддерживает установку обработчика на неотловленные прерывания (подобное ранее было доступно только в ThreadGroup).

Хорошим примером новых возможностей является синхронизация коллекции типа Hashtable. Объект Hashtable синхронизируется целиком, и если один поток занял кэш остальные потоки вынуждены ожидать освобождения объекта. В случае же использования нового класса ConcurrentHashMap практически все операции чтения могут проходить одновременно, операции чтения и записи практически не задерживают друг друга, и только одновременные попытки записи могут блокироваться. В случае же использования данного класса как кэша (спецификой которого является большое количество операций чтения и малое – записи), блокироваться многопоточный код практически не будет.

В таблице приведено время выполнения (в миллисекундах) программы, использовавшей в качестве кэша ConcurrentHashMap и Hashtable. Тесты проводились на двухпроцессорном сервере под управлением Linux. Количество данных для большего количества потоков увеличивалось.

Количество потоков ConcurrentHashMap Hashtable
  1.00 1.03
  2.59 32.40
  5.58 78.23
  13.21 163.48
  27.58 341.21
  57.27 778.41

// пример # 13: применение семафора: Sort.java: ArraySort.java

package chapt14;

import java.util.concurrent.*;

 

public class Sort {

public static final int ITEMS_COUNT = 15;

public static double items [];

// семафор, контролирующий разрешение на доступ к элементам массива

public static Semaphore sortSemaphore =

new Semaphore(0, true);

 

public static void main(String[] args) {

items = new double [ ITEMS_COUNT ];

for (int i = 0; i < items.length; ++i)

items [i] = Math. random ();

new Thread(new ArraySort(items)).start();

for (int i = 0; i < items. length; ++i) {

/*

* при проверке доступности элемента сортируемого

* массива происходит блокировка главного потока

* до освобождения семафора

*/

sortSemaphore. acquireUninterruptibly();

System. out. println(items [i]);

}

}

}

class ArraySort implements Runnable {

private double items[];

 

public ArraySort(double items[]) {

this. items = items;

}

public void run(){

for (int i = 0; i < items.length - 1; ++i) {

for (int j = i + 1; j < items.length; ++j) {

if (items[i] < items[j]) {

double tmp = items[i];

items[i] = items[j];

items[j] = tmp;

}

}

// освобождение семафора

Sort. sortSemaphore. release();

try {

Thread. sleep (71);

} catch (InterruptedException e) {

System. err. print(e);

}

}

Sort. sortSemaphore. release();

}

}

Задания к главе 14

Вариант А

1. Cоздать апплет с использованием потоков: строка движется горизонтально, отражаясь от границ апплета и меняя при этом случайным образом свой цвет.

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

3. Организовать сортировку массива методами Шелла, Хоара, пузырька, на основе бинарного дерева в разных потоках.

4. Реализовать сортировку графических объектов, используя алгоритмы из задания 3.

5. Создать апплет с точкой, движущейся по окружности с постоянной угловой скоростью. Сворачивание браузера должно приводить к изменению угловой скорости движения точки для следующего запуска потока.

6. Изобразить точку, пересекающую с постоянной скоростью окно слева направо (справа налево) параллельно горизонтальной оси. Как только точка доходит до границы окна, в этот момент от левого (правого) края с вертикальной координатной y, выбранной с помощью датчика случайных чисел, начинает свое движение другая точка и т.д. Цвет точки также можно выбирать с помощью датчика случайных чисел. Для каждой точки создается собственный поток.

7. Изобразить в приложении правильные треугольники, вращающиеся
в плоскости экрана вокруг своего центра. Каждому объекту соответствует поток с заданным приоритетом.

8. Условие предыдущей задачи изменяется таким образом, что центр вращения перемещается от одного края окна до другого с постоянной скоростью параллельно горизонтальной оси.

9. Cоздать фрейм с тремя шариками, одновременно летающими в окне.
С каждым шариком связан свой поток со своим приоритетом.

10. Два изображения выводятся в окно. Затем они постепенно исчезают
с различной скоростью в различных потоках (случайным образом выбираются точки изображения, и их цвет устанавливается в цвет фона).

11. Условие предыдущей задачи изменить на применение эффекта постепенного “проявления” двух изображений.

Вариант B

Для заданий варианта В главы 4 организовать синхронизированный доступ к ресурсам (файлам). Для каждого процесса создать отдельный поток выполнения.

Тестовые задания к главе 14

Вопрос 14.1.

Дан код:

class Q implements Runnable{

int i = 0;

public int run(){

System.out.println("i = "+ ++i);

return i;

}}

public class Quest1 {

public static void main(String[] args) {

Q ob = new Q();

ob.run();

}}

При попытке компиляции и запуска будет выведено:

1) i = 0;

2) i = 1;

3) ошибка компиляции: создать объект потока можно только с помощью инициализации объекта класса Thread или его наследников;

4) ошибка компиляции: неправильно реализован метод run();

5) ошибка времени выполнения: поток должен запускаться методом start().

Вопрос 14.2.

Дан код:

Thread t1= new Thread();

t1.setPriority(7);

ThreadGroup tg= new ThreadGroup("TG");

tg.setMaxPriority(8);

Thread t2= new Thread(tg,"t2");

System.out.print("приоритет t1="

+ t1.getPriority());

System.out.print(", приоритет t2="

+ t2.getPriority());

В результате компиляции и выполнения будет выведено:

1) приоритет t1 = 7, приоритет t2 = 5;

2) приоритет t1 = 7, приоритет t2 = 8;

3) приоритет t1 = 10, приоритет t2 = 8;

4) нет правильного.

Вопрос 14.3.

Дан код:

class T1 implements Runnable{

public void run(){

System.out.print("t1 ");

} }

class T2 extends Thread{

public void run(){

System.out.print("t2 ");

} }

public class Quest3 {

public static void main(String[] args) {

T1 t1 = new T1();

T2 t2 = new T2(t1);

t1.start();

t2.start();

} }

В результате компиляции и запуска будет выведено:

1) t1 t2;

2) t2 t1;

3) ошибка компиляции: метод start() не определен в классе T1;

4) ошибка компиляции: в классе T2 не определен конструктор, принимающий в качестве параметра объект Thread;

5) ничего из перечисленного.

Вопрос 14.4.

Какое из указанных действий приведет к тому, что поток переходит в состояние “пассивный”? (выберите два)

1) вызов метода sleep() без параметра;

2) вызов метода stop();

3) окончание выполнения метода run();

4) вызов метода notifyAll();

5) вызов метода wait() с параметром null.

Вопрос 14.5.

Дан код:

class Quest5 extends Thread {Quest5 () { }Quest5 (Runnable r) { super(r); } public void run() { System.out.print("thread "); } public static void main(String[] args) { Runnable r = new Quest5(); //1Quest5 t = new Quest5(r); //2 t.start(); } }

При попытке компиляции и запуска будет выведено:

1) ошибка компиляции в строке //1;2) ошибка компиляции в строке //2;3) thread;4) thread thread; 5) код будет откомпилирован, но ничего выведено не будет.

 
Глава 15

СЕТЕВЫЕ ПРОГРАММЫ

Поддержка Интернет

Язык Java делает сетевое программирование простым благодаря наличию специальных средств и классов. Большинство этих классов находится в пакете java.net. Сетевые классы имеют методы для установки сетевых соединений передачи запросов и сообщений. Многопоточность позволяет обрабатывать несколько соединений. Сетевые приложения используют Internet-приложения, к которым относятся Web-браузер, e-mail, сетевые новости, передача файлов. Для создания таких приложений используются сокеты, порты, протоколы TCP/IP, UDP.

Приложения клиент/сервер используют компьютер, выполняющий специальную программу-сервер, которая обычно устанавливается на удаленном компьютере и предоставляет услуги другим программам-клиентам. Клиент - это программа, получающая услуги от сервера. Клиент устанавливает соединение с сервером и пересылает серверу запрос. Сервер осуществляет прослушивание клиентов, получает и выполняет запрос после установки соединения. Результат выполнения запроса может быть возвращен сервером клиенту. Запросы и сообщения представляют собой записи, структура которых определяется используемыми протоколами.

В стеке протоколов TCP/IP используются следующие прикладные протоколы:

HTTP - Hypertext Transfer Protocol (WWW);

NNTP - Network News Transfer Protocol (группы новостей);

SMTP - Simple Mail Transfer Protocol (посылка почты);

POP3 - Post Office Protocol (чтение почты с сервера);

FTP - File Transfer Protocol (протокол передачи файлов).

Каждый компьютер из подключенных к сети по протоколу TCP/IP имеет уникальный IP-адрес, используемый для идентификации и установки соединения. Это 32-битовое число, обычно записываемое как четыре числа, разделенные точками, каждое из которых изменяется от 0 до 255. IP-адрес может быть временным и выделяться динамически для каждого подключения или быть постоянным, как для сервера. IP-адреса используются во внутренних сетевых системах. Обычно при подключении к Internet вместо числового IP-адреса используются символьные имена (например: www.bsu.by), называемые именами домена. Специальная программа DNS (Domain Name Server), располагаемая на отдельном сервере, проверяет адрес и преобразует имя домена в числовой IP-адрес. Если в качестве сервера используется этот же компьютер без сетевого подключения, в качестве IP-адреса указывается 127.0.0.1 или localhost. Для явной идентификации услуг к IP-адресу присоединяется номер порта через двоеточие, например 217.21.43.10:443. Здесь указан номер порта 443. Номера портов от 1 до 1024 могут быть заняты для внутреннего использования, например, если порт явно не указан, браузер воспользуется значением по умолчанию: 20FTP -данные, 21FTP -управление, 53DNS, 80HTTP, 25SMTP, 110POP3, 119NNTP. К серверу можно подключиться с помощью различных портов. Каждый порт указывает конкретное место соединения на указанном компьютере и предоставляет определенную услугу.

Для доступа к сети Internet в браузере указывается адрес URL. Адрес URL (Universal Resource Locator) состоит из двух частей – префикса протокола (http, https, ftp и т.д.) и URI (Universal Resource Identifier). URI содержит Internet-адрес, необязательный номер порта и путь к каталогу, содержащему файл, например:

https://www.bsu.by

URI не может содержать такие специальные символы, как пробелы, табуляции, возврат каретки. Их можно задавать через шестнадцатеричные коды. Например: %20 обозначает пробел. Другие зарезервированные символы: символ & –разделитель аргументов, символ? –следует перед аргументами запросов, символ + – пробел, символ # – ссылки внутри страницы (имя_страницы#имя_ссылки).

Определить IP-адрес в приложеннии можно с помощью объекта класса InetAddress из пакета java.net.

Класс InetAddress не имеет public -конструкторов. Создать объект класса можно с помощью статических методов. Метод getLocalHost() возвращает объект класса InetAddress, содержащий IP-адрес и имя компьютера, на котором выполняется программа. Метод getByName(String host) возвращает объект класса InetAddress, содержащий IP-адрес по имени компьютера, используя пространство имен DNS. IP-адрес может быть временным, различным для каждого соединения, однако он остается постоянным, если соединение установлено. Метод getByAddress(byte[] addr) создает объект класса InetAddress, содержащий имя компьютера, по IP-адресу, представленному в виде массива байт. Если компьютер имеет несколько IP, то получить их можно методом getAllByName(String host),возвращающим массив объектов класса InetAddress. Если IP для данной машины один, то массив будет содержать один элемент. Метод getByAddress(String host, byte[] addr) создает объект класса InetAddress с заданным именем и IP-адресом, не проверяя существование такого компьютера. Все эти методы являются потенциальными генераторами исключительной ситуации UnknownHostException, и поэтому их вызов должен быть обработан с помощью throws для метода или блока try-catch. Проверить доступ к компьютеру в данный момент можно с помощью метода boolean isReachable(int timeout), который возвращает true, если компьютер доступен, где timeout – время ожидания ответа от компьютера в миллисекундах.

Следующая программа демонстрирует при наличии Internet-соединения, как получить IP-адрес текущего компьютера и IP-адрес из имени домена с помощью сервера имен доменов (DNS), к которому обращается метод getByName() класса InetAddress.

/* пример # 1: вывод IP-адреса компьютера и интернет-ресурса: InetLogic.java*/

package chapt15;

import java.net.*;

 

public class InetLogic {

public static void main(String[] args) {

InetAddress myIP = null;

InetAddress bsuIP = null;

try {

myIP = InetAddress.getLocalHost();

// вывод IP-адреса локального компьютера

System.out.println("Мой IP -> "

+ myIP.getHostAddress());

bsuIP = InetAddress.getByName(

"www.bsu.by");

// вывод IP-адреса БГУ www.bsu.by

System.out.println("BSU -> "

+ ibaIP.getHostAddress());

} catch (UnknownHostException e) {

// если не удается найти IP

e.printStackTrace();

}

}

}

В результате будет выведено, например:

Мой IP -> 172.17.16.14

BSU -> 217.21.43.10

Метод getLocalHost() класса InetAddress создает объект myIP и возвращает IP-адрес компьютера.

/* пример # 2: присваивание фиктивного имени реальному ресурсу, заданному через IP: UnCheckedHost.java */

package chapt15;

import java.io.IOException;

import java.net.InetAddress;

import java.net.UnknownHostException;

 

public class UnCheckedHost {

public static void main(String[] args) {

// задание IP-адреса лаборатории bsu.iba.by в виде массива

byte ip[] ={(byte)217, (byte)21, (byte)43, (byte)10};

try {

InetAddress addr =

InetAddress.getByAddress("University", ip);

System. out. println(addr.getHostName()

+ "-> cоединение:" + addr.isReachable(1000));

} catch (UnknownHostException e) {

System. out. println("адрес недоступен");

e.printStackTrace();

} catch (IOException e) {

System. out. println("ошибка потока");

e.printStackTrace();

}

}

}

В результате будет выведено в случае подключения к сети Интернет:

University-> cоединение:true

Для доступа к ресурсам можно создать объект класса URL, указывающий на ресурсы в Internet. В следующем примере объект URL используется для доступа к HTML-файлу, на который он указывает, и отображает его в окне браузера с помощью метода showDocument().

/* пример # 3: запуск страницы из апплета: MyShowDocument.java */

package chapt15;

import java.awt.Graphics;

import java.net.MalformedURLException;

import java.net.URL;

import javax.swing.JApplet;

 

public class MyShowDocument extends JApplet {

private URL bsu = null;

 

public String getMyURL() {

return "https://www.bsu.by";

}

public void paint(Graphics g) {

int timer = 0;

g.drawString("Загрузка страницы", 10, 10);

try {

for (; timer < 30; timer++) {

g.drawString(".", 10 + timer * 5, 25);

Thread. sleep (100);

}

bsu = new URL(getMyURL());



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-02-16 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: