SlideShare uma empresa Scribd logo
1 de 75
Лекция №10
                            «Threads»



Автор: доц. каф. ПО ЭВМ ХНУРЭ Колесников Д.О.   v.0.2
10.1. Выполнение инструкций потоками

Поток выполнения – это последовательность команд,
выполняемых процессором.

Другие названия потока выполнения: поток вычисления,
нить, (англ.) thread.

Выполняемая программа может иметь несколько потоков.

Если в программе выполняется некоторая инструкция
(вызов метода, оператор, операция), то ее всегда
выполняет некоторый поток.
10.2. Суперкласс потоков выполнения

Любой класс поток     является   наследником   класса
java.lang.Thread.

Выражение «на потоке вызван метод» следует понимать
как вызов метода на объекте соответствующего класса
потока.

Выражение «поток выполняет метод» следует понимать
как выполнение инструкций метода потоком.
10.3. Главный поток

Любая программа имеет хотя бы один поток вычислений –
главный поток.
Точкой входа в главный поток является метод main.

public class test {
      public static void main(String[] argv) {
              // инструкции метода main
              // выполняет главный поток программы
      }
}

Главный поток запускает JVM.
Все инструкции метода main выполняет главный поток.
10.4. Статические методы класса Thread

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

Например, ссылку на объект Thread текущего потока
можно получить с помощью статического метода

     Thread.currentThread();
10.5. Имя потока

Любому потоку можно присвоить имя – либо с помощью
конструктора, либо с помощью метода setName. Имя
потока возвращает метод getName (оба метода setName и
getName определены в классе Thread).

public static void main(String[] argv) {
      Thread.currentThread().setName("main");
      System.out.println(
              Thread.currentThread().getName()); // main
}

Замечание. JVM не использует имена потоков, они служат
только для удобства программиста. Двум разным потокам
можно присвоить одно и то же имя.
10.6. Создание и запуск дочернего потока
                     (extends Thread)

Чтобы создать поток нужно расширить класс Thread,
перекрыв его метод run

class B extends Thread {
       public void run() {
             // do something
       }
}
После создания поток можно запустить на выполнения с
помощью метода start класса Thread.

B b = new B();
b.start();
// или
new B().start();

Замечание. Метод run является точкой входа в поток.
Метод main является точкой входа в главный поток (его
запускает JVM).
10.7. Создание и запуск дочернего потока
                  (implements Runnable)

Другой способ создания потока заключается в реализации
интерфейса Runnable, который имеет один метод run.

class B implements Runnable {
       public void run() {
             // do something
       }
}
После этого создают новый поток с помощью
конструктора Thread(Runnable target).

Thread t = new Thread(new B());
t.start();
// или
new Thread(new B()).start();


Замечание. Класс Thread реализует интерфейс Runnable.

public class Thread implements Runnable
Замечание. В качестве параметра конструктору Thread
может быть передан класс, расширяющий класс Thread.

public class Test {
      public static void main(String[] argv) {
             Thread t = new Thread(new MyThread());
             t.start();
      }
}

class MyThread extends Thread {
      public void run() {}
}
10.8. Создание и запуск потока в одном классе

Поток можно создать и запустить в одном классе.

public class MyThread extends Thread {
      public void run() {
             ...
      }

     public static void main(String[] argv) {
           // главный поток запускает новый поток
           new MyThread().start();
     }
}
10.9. Запуск потока в конструкторе класса-потока

Часто поток запускают в конструкторе потока.

class MyThread extends Thread {
      public MyThread() {
            this.start();
      }
      public void run() {
             // do something
      }
      public static void main(String[] argv) {
            new MyThread ();
      }
}
10.10. Создание и запуск потока с помощью
                  анонимного класса

Поток можно создать и запустить в методе с помощью
анонимного класса.

public class MyThread extends Thread {
      public static void main(String[] argv) {
             new Thread() {
                    public void run() {
                           // do something
                    }
             }.start();
      }
}
10.11. Завершение выполнения потока

Поток начинает свое выполнение, когда на нем вызовут
метод start (в родительском потоке). Метод start в свою
очередь вызывает метод run.

Поток завершает свое выполнение после выполнения
последней инструкции метода run.

Аналогия для главного потока – главный поток завершает
свое выполнение после выполнения последней
инструкции метода main.

Возможен выход из потоков в связи с выбросом
исключений.
Замечание. Существуют т.н. «потоки-демоны», которые
предназначены для обслуживания других потоков.

Если в программе запущенными остаются только потоки-
демоны, то JVM принудительно прекращает их работу и
завершает выполнение приложения.

Чтобы сделать поток «демоном» необходимо перед его
запуском вызвать на нем метод setDaemon с
соответствующим значением.
public class test extends Thread {
      public void run() { while(true); } // бесконечный цикл

      public static void main(String[] argv)
                     throws InterruptedException {
              // главный поток создает поток test
             test t = new test();
             t.setDaemon(true); // делает его «демоном»
             t.start(); // запускает
             // чтобы дочерний поток успел запуститься:
             Thread.sleep(1000);
      }// здесь главный поток завершает свое
        // выполнение и JVM завершает работу
       // приложения прерывая бесконечный цикл
}
10.12. Метод sleep класса Thread

Класс Thread содержит статический метод sleep, который
делает паузу в выполнении текущего потока на заданное
число миллисекунд.

public static void sleep(long millis)
      throws InterruptedException
Метод может выбросить исключение InterruptedException
в том случае, если на потоке, для которого он делает
паузу, будет вызван метод interrupt.

public static void main(String[] argv) {
      for (int j = 0; j < 10; j++) {
              System.out.println(j);
               // главный поток переводится
              // в режим паузы примерно на 1 секунду
              try {
                     Thread.sleep(1000);
              }
              catch (Exception e) {e.printStackTrace();}
      }
}
public class test extends Thread {
      public void run() {
              for (int j = 0; j < 10; j++) {
                      System.out.println(j);
                      try {Thread.sleep(200);}
                      catch (Exception e) {e.printStackTrace();}
              }
      }
      public static void main(String[] args) throws
                      InterruptedException {
              test t = new test(); t.start();
              Thread.sleep(1000);
              t.interrupt(); // через секунду будет выброс
              // исключения методом sleep если он
      }       // выполняется в данный момент
}
Замечание. Метод sleep перегружен. Точность времени
паузы не гарантируется.

// пауза на ms миллисекунд
public static void sleep(long ms)

// пауза на ms миллисекунд и ns наносекунд
public static void sleep(long ms, int ns)

Замечание. InterruptedException наследуется напрямую от
Exception, поэтому необходимо предусмотреть обработку
этого исключения.
10.13. Метод alive класса Thread

Метод isAlive класса Thread возвращает true, если поток,
на котором он вызван, запущен и еще не прекратил свое
выполнение.
public class test extends Thread {
      public static void main(String[] argv) {
              // гл. поток создает и запускает новый поток
              test t = new test();
              t.start();
              // главный поток вызывает метод
              // isAlive на запущенном потоке:
              while (boolean isAlive = t.isAlive()) {
                       // будет выводится true пока
                      // выполняется запущенный поток:
                      System.out.println(isAlive);
              }       // число циклов заранее неизвестно
      }               // зависит от того, как долго будет
                      // выполняться метод run
      public void run() {}
}
10.14. Пример запуска двух потоков, которые выводят
     разные сообщения с разной периодичностью

public class test extends Thread {
      private String mes;
      private int ms;

      // конструктор потока
      public test(String mes, int ms) {
             this.mes = mes;
             this.ms = ms;
             // запуск потока в конструкторе
             this.start();
      }
public void run() {
           while (true) { // бесконечный цикл
                   // вывод сообщения
                  System.out.println(this.mes);
                   // пауза на ms миллисекунд
                  try {sleep(this.ms);}
                  catch (Exception e) {}
           }
    }
    // главный поток запускает три потока
    public static void main(String[] argv) {
           new test("A", 500); // первый
           new test("B", 700); // второй
           new test("C", 1000); // третий
    }
}
10.15. Единственность способа запуска потока.

Запуск потока возможен только с помощью метода start.

Вызов метода run на объекте потоке приведет лишь к
одноразовому выполнению тела этого метода в том
потоке, который вызвал этот метод.
public class test extends Thread {
      public void run() {
              Thread t = Thread.currentThread();
              System.out.println(t.getName());
      }
      public static void main(String[] argv) {
              // главному потоку задаем имя main
              Thread.currentThread().setName("main");
              test t = new test();
              // дочернему потоку задаем имя thread-0
              t.setName("thread-0");
              t.run(); // main
              t.start(); // thread-0
      }
}
Замечание. JVM задает каждому потоку некоторое
системное имя при его создании

test t = new test();

Это имя возвращает метод getName, если его вызывать на
соответствующем объекте-потоке.

t.getName();

Если требуется получить имя потока, который выполняет
некоторый метод, достаточно в код метода вставить
следующую инструкцию:

String threadName = Thread.currentThread().getName();
10.16. Проблема параллельного выполнения одного
               кода разными потоками

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

public class test {
      private boolean flag;
       // выполняют два потока параллельно
      public void m(boolean flag) {
              this.flag = flag;
              try {Thread.sleep(200);} // ждем
              catch (InterruptedException e)
              {e.printStackTrace();}
              System.out.println(this.flag + " == " + flag);
      } // true == false
public test() { // конструктор создает два потока
          new Thread() { // создание первого потока
                  public void run() {
                          while (true) m(true);
                  }
          }.start(); // запуск первого потока
          new Thread() { // создание второго потока
                  public void run() {
                          while (true) m(false);
                  }
          }.start(); // запуск второго потока
    }
    public static void main(String[] argv) {
          new test();
    }
}
10.17. Синхронизация

Для разрешения проблемы параллельного доступа разных
потоков к одному и тому же коду применяется
синхронизация.

Синхронизации может быть подвергнут метод
public synchronized void m() {...}

статический метод
public static synchronized void m() {...}

участок кода метода.
public void m() {
      synchronized(o) {...}
}
Синхронизация осуществляется с помощью специального
объекта-монитора, который связан с этой синхронизацией.

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

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

Монитором всегда является объект, который зависит от
того, какой код синхронизируется.
  синхронизируемый                монитор
         код
нестатический метод объект this
                      объект типа Class,
статический метод     соответствующий классу, в
                      котором определен метод
                      произвольный объект o -
участок кода метода   параметр инструкции
                      synchronized
Замечание. Монитором не может быть null.

Замечание. Т.к. JVM создает уникальный объект типа
Class, для каждого загруженного класса, то для
статических синхронизированных методов существует
один и только один монитор.

Для нестатического синхронизированного метода может
существовать несколько мониторов – по числу созданных
объектов класса, в котором определен этот метод.
public class test extends Thread {
      public synchronized void m() {
              while (true) System.out.println(
                     Thread.currentThread().getName());
      }

      public test() {this.start();}
      public void run() {this.m();}

      public static void main(String[] argv) {
             test treadA = new test(); // запуск потока A
             test threadB = new test(); // запуск потока B
      }
      // в процессе выполнения будут выводиться
      // имена потоков threadA и threadB вперемешку
}
В предыдущем примере два потока A и B выполняют
один и тот же метод одновременно, т.к. они блокируют
разные мониторы: threadA и threadB соответственно
(синхронизация не работает).

Если метод m сделать статическим, то на экран сообщения
будет выводить только один поток – A.
Второй поток B будет бесконечно долго ждать, пока
первый не разблокирует монитор, который в данном
случае один - объект класса Class, соответствующий
классу test (test.class).
10.19. Инструкция synchronized

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

public class test {
      // объект-монитор
      private Object lockA = new Object();
      public void m() {
              synchronized (lockA) {... }
      }
}

Замечание. Параметром инструкции synchronized всегда
является объект.
10.20. Метод join класса Thread

Класс Thread содержит метод join предназначенный для
перевода потока, который вызвал этот метод в режим
паузы до тех пор, пока не закончит свою работу поток, на
котором этот метод был вызван.

Замечание. Метод может выбросить исключение
InterruptedException, если на потоке, который находится в
режиме паузы, будет вызван метод interrupt.

Замечание. Поток, выполняющий метод join не снимает
блокировки с мониторов, которые он заблокировал.
class test extends Thread {
       public void run() {
              try {sleep(2000);}
              catch (Exception e) {e.printStackTrace();}
       }
       public static void main(String[] argv)
                            throws Exception{
               // создание и запуск дочернего потока:
              test t = new test();
              t.start();
              // главный поток ждет завершения
              // дочернего потока:
              t.join();
              System.out.println("pause has expired");
       }
}
10.21. Метод wait класса Object

Метод wait переводит поток в режим ожидания. Метод
имеет три варианта.

 wait()                бесконечное ожидание
 wait(long ms)         ожидание ms миллисекунд
 wait(long ms, int ns) ожидание ms миллисекунд и ns
                       наносекунд

Замечание. wait(0) эквивалентно wait()
Метод wait должен вызываться
(1) только из синхронизированного кода
(2) на объекте мониторе, связанным с данным
синхронизированным кодом.
(3) поток, который вызывает эти методы, должен владеть
монитором.

wait может выбросить исключение InterruptedException.

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

Замечание. Метод wait определен в классе Object.
Монитором может быть любой объект.
Замечание. Когда поток выполняет метод wait возможны
т.н. случайные пробуждения. Поэтому выполнение метода
wait следует заключать в цикл с проверкой условия.

Object.java:

  * As in the one argument version, interrupts and spurious
  * wakeups are possible, and this method should always be
  * used in a loop:
  * synchronized (obj) {
  *      while (<condition does not hold>)
  *          obj.wait(timeout, nanos);
  *      ... // Perform action appropriate to condition
  * }
public class test extends Thread {
       // точка входа в поток:
      public void run() {
              // синхронизированный блок (монитор = this):
              synchronized (this) {
                      // перевод this в режим ожидания:
                     try {wait();}
                     catch (Exception e) {
                            // вывести сообщение после
                            // прерывания:
                            System.out.println(
                                   "thread has been interrupted");
                     }
              }
      }
public static void main(String[] argv)
                        throws Exception {
          // создание и запуск потока:
          test t = new test();
          t.start();

          // пауза в главном потоке, чтобы успел
          // начать свое выполнение метод wait:
          Thread.sleep(500);

          // прерывать запущенный поток:
          t.interrupt();
    }
}
10.22. Методы notify и notifyAll класса Object

Для естественного выхода из режима ожидания
применяются методы notify и notifyAll, которые должны:
(1) вызываться на объекте-мониторе
(2) только из синхронизированного кода.
(3) поток, который вызывает эти методы, должен владеть
монитором.

На одном и том же мониторе может находиться несколько
потоков в режиме ожидания.

Метод notify пробуждает только один случайно
выбранный поток; notifyAll пробуждает все потоки,
которые ожидают на этом мониторе.
Замечание. Методы notify и notifyAll определены в классе
Object, т.к. монитором потенциально может являться
любой объект.

Замечание. Методы notify и notifyAll выбрасывают
IllegalMonitorStateException с сообщением «current thread
not owner», если поток, который вызывает эти методы, не
является владельцем монитора.

Замечание. Нежелательно использовать метод notify, т.к.
неизвестно какой именно поток получит уведомление.
public class test extends Thread {
      public void run() {
              // монитором является this /*1*/
              synchronized (this) {
                     try {
                           // перевод в режим ожидания:
                           wait();
                           // вывод сообщения при
                           // пробуждении:
                           System.out.println(
                                  "thread has been notified");
                     } catch (Exception e) {
                           e.printStackTrace();
                     }
              }
      }
public static void main(String[] argv)
                        throws Exception {
          test t = new test();
          t.start();
          Thread.sleep(500);

          // главный поток входит в монитор t
          // (= this в строке /*1*/)
          synchronized (t) {
                 // и выполняет вызов notify на мониторе
                 // которым владеет главный поток
                 t.notify();
          }
    }
}
10.23. Блокированное состояние
                 пробужденного потока

Если поток находился в состоянии ожидания, выполняя
метод wait и был пробужден методом notify/notifyAll то он
находиться в блокированном состоянии до тех пор, пока
не освободиться монитор и только после этого
продолжает выполнять первую инструкцию за методом
wait.
public class test extends Thread {
      public void run() {
              // "abc" – уникальный строковый литерал
              synchronized ("abc") {
                     try {
                            // поток вызывает на мониторе
                            // "abc" метод wait
                            "abc".wait();
                            // вывод на экран после
                            // выполнения строки /*1*/
                            System.out.println(
                                   "thread has been notified");
                     }
                     catch (Exception e) {e.printStackTrace();}
              }
      }
public static void main(String[] argv)
                         throws Exception {
          new test().start();
          Thread.sleep(500);
          synchronized ("abc") {
                  // главный поток вызывает на
                  // мониторе "abc" метод notifyAll
                  "abc".notifyAll();

                // главный поток переводится в режим
                // паузы на примерно на 10 с. /*1*/
                Thread.sleep(10000);
          }
    }
}
10.24. Совместное использование методов
                 wait и notify/notifyAll

Совместное использование методов notify/notifyAll и wait
дает возможность потокам синхронизировать свое
выполнение.

Замечание. Если на мониторе нет ожидающих потоков, то
вызов методов notify/notifyAll не генерирует никаких
исключительных ситуаций.
public class test extends Thread {
      public static Object lock = new Object();
      private String mes;
      public test(String mes) {this.mes = mes;}
      public void run() {
              synchronized (test.lock) {
                     try {
                            while (true) {
                                  test.lock.wait();
                                  test.lock.notify();
                                  System.out.println(this.mes);
                            }
                     }
                     catch (Exception e) {e.printStackTrace();}
              }
      }
public static void main(String[] argv)
                         throws Exception {
          // после запуска оба потока переходят в
          // режим ожидания, выполняя wait
          new test("A").start(); new test("B").start();
          // пауза, чтобы запущенные потоки успели
          // начать выполнение wait
          Thread.sleep(500);
          synchronized (test.lock) {
                  // для оповещения всех ждущих на
                  // мониторе потоков нужно вызвать на
                  // нем метод notifyAll
                  test.lock.notifyAll(); //
          }
    }
}
Замечание. Потоки могут синхронизировать             свое
выполнение без участия третьего (главного) потока.


public class test extends Thread {
      private String mes;
      public test(String mes) {this.mes = mes;}

      public static void main(String[] argv)
                          throws Exception {
            new test("A").start();
            new test("B").start();
      }
public void run() {
              // монитор - объект класса Class,
             // соответствующий классу test
             synchronized (test.class) {
                    try {
                           while (true) {
                                 test.class.notify();
                                 test.class.wait();
                                 System.out.println(this.mes);
                           }
                    }
                    catch (Exception e) {e.printStackTrace();}
             }
      }
}
10.25. Взаимная блокировка

Потоки могут входить в состояние взаимной блокировки.
public class test extends Thread {
      public static Object lockA = new Object();
      public static Object lockB = new Object();

      public void run() {
            try {
                   synchronized (test.lockA) {
                          // после паузы поток ждет, пока
                          // будет снята блокировка с
                          // монитора test.lockB
                          sleep(500);
                          synchronized (test.lockB) {}
                   }
            } catch (Exception e) {e.printStackTrace();}
      }
public static void main(String[] argv)
                          throws Exception {
          new test().start();
          synchronized (test.lockB) {
                  // после паузы поток ждет, пока будет
                  // снята блокировка с монитора
                  // test.lockA
                  sleep(300);
                  synchronized (test.lockA) {}
          }
    }
}
10.26. Состояния потоков

Различают следующие состояния потока:

1) созданный;
2) запущенный;
3) блокированный;
4) остановленный.

Замечание. Не существует средств различения состояний
«поток запущен» и «поток блокирован».
10.27. Случаи блокирования
            и разблокирования потоков

Блокирование и разблокирование потока осуществляется в
следующих случаях.
причина блокирования потока:
поток вызывает метод sleep

момент разблокирования потока:
1) истечение времени паузы
2) на этом потоке вызван метод interrupt

при блокировке потока блокировка с монитора, которым
он владеет:
не снимается
причина блокирования потока:
поток начинает выполнять операцию ввода/вывода

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

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

момент разблокирования потока:
снятие блокировки с монитора

при блокировке потока блокировка с монитора, которым
он владеет:
не снимается
причина блокирования потока:
поток вызывает метод join

момент разблокирования потока:
1) поток, на котором вызван метод join, завершает свое
выполнение
2) на этом потоке вызван метод interrupt

при блокировке потока блокировка с монитора, которым
он владеет:
не снимается
причина блокирования потока:
поток начинает выполнять метод wait и переходит в
режим ожидания на этом мониторе

момент разблокирования потока:
1) истечение времени ожидания
2) на этом потоке вызван метод interrupt
3) на мониторе вызван метод notify/notifyAll
4) случайное пробуждение

при блокировке потока блокировка с монитора, которым
он владеет:
снимается
10.28. Метод interrupt класса Thread

Если поток находится в блокированном состоянии в
результате выполнения методов sleep, join или wait, а
другой поток вызывает на этом потоке метод interrupt, то
соответствующие методы прекращают свое выполнение и
выбрасывают исключение InterruptedException.
public class test extends Thread {
      public void run() {
              try {
                     sleep(10000); // InterruptedException
                     this.join(); // InterruptedException
                     synchronized (test.class) {
                            // InterruptedException:
                            test.class.wait();
                     }
              } catch (Exception e) {
                     e.printStackTrace();
              }
      }
public static void main(String[] argv)
                         throws Exception {
          test t = new test();
          t.start();
          t.interrupt();
    }
}
10.29. Методы interrupted и isInterrupted
                   класса Thread

Потоки имеют внутренний флаг, который определяет, был
ли прерван поток методом interrupt. Для проверки этого
флага применяются методы interrupted и isInterrupted.

public static boolean interrupted()
проверяет был ли текущий поток прерван и сбрасывает
внутренний флаг в false.

public boolean isInterrupted()
проверяет был ли поток (на котором данный метод
вызван) прерван, внутренний флаг при этом остается без
изменений.
Замечание. Методы wait, sleep и join выбросив
исключение InterruptedException сбрасывают флаг
прерывания потока в false.

class test {
       public static void main(String[] argv) {
             Thread t = Thread.currentThread();
             t.interrupt();
             System.out.println(t.isInterrupted()); // true
             System.out.println(t.isInterrupted()); // true
             System.out.println(Thread.interrupted()); // true
             System.out.println(Thread.interrupted()); // false
       }
}
10.30. Изменение значения аргумента
                блока синхронизации

Изменение значения аргумента синхронизированного
блока не приводит к смене монитора в нем (имеется ввиду
присваивание аргументу значения нового объекта).

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

public class test extends Thread {
      Object monitor = "123";
      public void m() throws Exception {
              synchronized (monitor) {
                     System.out.println(
                           "montior1 = " + monitor);
                     monitor = "abc";
                     Thread.sleep(5000);
                     System.out.println(
                           "monitor2 = " + monitor);
              }
      }
public void run() {
          try {m();}
          catch (Exception E) {}
    }
    public static void main(String[] argv)
                        throws Exception {
          test t = new test();
          t.start();
          Thread.sleep(1000);
          synchronized ("123") {
                  System.out.println(
                        monitor3 = " + t.monitor);
          }
    }
}
Программа выведет       в   стандартный    поток   вывода
следующие строки:

montior1 = 123
monitor2 = abc
monitor3 = abc

т.е., блокировка с объекта "123" не снимается.

Mais conteúdo relacionado

Mais procurados

Java. Многопоточность.
Java. Многопоточность.Java. Многопоточность.
Java. Многопоточность.Unguryan Vitaliy
 
C++ STL & Qt. Занятие 10.
C++ STL & Qt. Занятие 10.C++ STL & Qt. Занятие 10.
C++ STL & Qt. Занятие 10.Igor Shkulipa
 
Системы контроля версий
Системы контроля версийСистемы контроля версий
Системы контроля версийUnguryan Vitaliy
 
C++ STL & Qt. Занятие 11.
C++ STL & Qt. Занятие 11.C++ STL & Qt. Занятие 11.
C++ STL & Qt. Занятие 11.Igor Shkulipa
 
Java Core. Lecture# 3. Part# 3. Multithreading.
Java Core. Lecture# 3. Part# 3. Multithreading.Java Core. Lecture# 3. Part# 3. Multithreading.
Java Core. Lecture# 3. Part# 3. Multithreading.Anton Moiseenko
 
C++ STL & Qt. Занятие 08.
C++ STL & Qt. Занятие 08.C++ STL & Qt. Занятие 08.
C++ STL & Qt. Занятие 08.Igor Shkulipa
 
Java осень 2014 занятие 3
Java осень 2014 занятие 3Java осень 2014 занятие 3
Java осень 2014 занятие 3Technopark
 
Практика Lock-free. RealTime-сервер
Практика Lock-free. RealTime-серверПрактика Lock-free. RealTime-сервер
Практика Lock-free. RealTime-серверPlatonov Sergey
 
C++ STL & Qt. Занятие 01.
C++ STL & Qt. Занятие 01.C++ STL & Qt. Занятие 01.
C++ STL & Qt. Занятие 01.Igor Shkulipa
 
МАИ, Сети ЭВМ, Лекция №4
МАИ, Сети ЭВМ, Лекция №4МАИ, Сети ЭВМ, Лекция №4
МАИ, Сети ЭВМ, Лекция №4Dima Dzuba
 
C++ STL & Qt. Занятие 02.
C++ STL & Qt. Занятие 02.C++ STL & Qt. Занятие 02.
C++ STL & Qt. Занятие 02.Igor Shkulipa
 
C++ STL & Qt. Занятие 04.
C++ STL & Qt. Занятие 04.C++ STL & Qt. Занятие 04.
C++ STL & Qt. Занятие 04.Igor Shkulipa
 
Multithreading in java past and actual
Multithreading in java past and actualMultithreading in java past and actual
Multithreading in java past and actualYevgen Levik
 
Oop java.generics
Oop java.genericsOop java.generics
Oop java.genericsmuqaddas_m
 
Java осень 2014 занятие 5
Java осень 2014 занятие 5Java осень 2014 занятие 5
Java осень 2014 занятие 5Technopark
 
C++ STL & Qt. Занятие 03.
C++ STL & Qt. Занятие 03.C++ STL & Qt. Занятие 03.
C++ STL & Qt. Занятие 03.Igor Shkulipa
 

Mais procurados (19)

Java. Многопоточность.
Java. Многопоточность.Java. Многопоточность.
Java. Многопоточность.
 
C++ STL & Qt. Занятие 10.
C++ STL & Qt. Занятие 10.C++ STL & Qt. Занятие 10.
C++ STL & Qt. Занятие 10.
 
Системы контроля версий
Системы контроля версийСистемы контроля версий
Системы контроля версий
 
C++ STL & Qt. Занятие 11.
C++ STL & Qt. Занятие 11.C++ STL & Qt. Занятие 11.
C++ STL & Qt. Занятие 11.
 
Java Core. Lecture# 3. Part# 3. Multithreading.
Java Core. Lecture# 3. Part# 3. Multithreading.Java Core. Lecture# 3. Part# 3. Multithreading.
Java Core. Lecture# 3. Part# 3. Multithreading.
 
C++ STL & Qt. Занятие 08.
C++ STL & Qt. Занятие 08.C++ STL & Qt. Занятие 08.
C++ STL & Qt. Занятие 08.
 
Java 8. Thread pools
Java 8. Thread poolsJava 8. Thread pools
Java 8. Thread pools
 
Java осень 2014 занятие 3
Java осень 2014 занятие 3Java осень 2014 занятие 3
Java осень 2014 занятие 3
 
Практика Lock-free. RealTime-сервер
Практика Lock-free. RealTime-серверПрактика Lock-free. RealTime-сервер
Практика Lock-free. RealTime-сервер
 
C++ STL & Qt. Занятие 01.
C++ STL & Qt. Занятие 01.C++ STL & Qt. Занятие 01.
C++ STL & Qt. Занятие 01.
 
МАИ, Сети ЭВМ, Лекция №4
МАИ, Сети ЭВМ, Лекция №4МАИ, Сети ЭВМ, Лекция №4
МАИ, Сети ЭВМ, Лекция №4
 
C++ STL & Qt. Занятие 02.
C++ STL & Qt. Занятие 02.C++ STL & Qt. Занятие 02.
C++ STL & Qt. Занятие 02.
 
C++ STL & Qt. Занятие 04.
C++ STL & Qt. Занятие 04.C++ STL & Qt. Занятие 04.
C++ STL & Qt. Занятие 04.
 
Multithreading in java past and actual
Multithreading in java past and actualMultithreading in java past and actual
Multithreading in java past and actual
 
Oop java.generics
Oop java.genericsOop java.generics
Oop java.generics
 
Gradle
GradleGradle
Gradle
 
Java осень 2014 занятие 5
Java осень 2014 занятие 5Java осень 2014 занятие 5
Java осень 2014 занятие 5
 
C++ STL & Qt. Занятие 03.
C++ STL & Qt. Занятие 03.C++ STL & Qt. Занятие 03.
C++ STL & Qt. Занятие 03.
 
9 free rtos
9 free rtos9 free rtos
9 free rtos
 

Semelhante a java

8. java lecture threads
8. java lecture threads8. java lecture threads
8. java lecture threadsMERA_school
 
JPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerJPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerAnton Arhipov
 
Svitla .Net meetup in Kiev, Anzhiiak Oleksii
Svitla .Net meetup in Kiev, Anzhiiak OleksiiSvitla .Net meetup in Kiev, Anzhiiak Oleksii
Svitla .Net meetup in Kiev, Anzhiiak OleksiiSvitla Systems Inc.
 
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)Noveo
 
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...Alexey Paznikov
 
Java весна 2014 лекция 5
Java весна 2014 лекция 5Java весна 2014 лекция 5
Java весна 2014 лекция 5Technopark
 
Programming Java - Lection 06 - Multithreading - Lavrentyev Fedor
Programming Java - Lection 06 - Multithreading - Lavrentyev FedorProgramming Java - Lection 06 - Multithreading - Lavrentyev Fedor
Programming Java - Lection 06 - Multithreading - Lavrentyev FedorFedor Lavrentyev
 
Исключения и ошибки
Исключения и ошибкиИсключения и ошибки
Исключения и ошибкиUnguryan Vitaliy
 
Многопоточное программирование на C#, путевые заметки
Многопоточное программирование на C#, путевые заметкиМногопоточное программирование на C#, путевые заметки
Многопоточное программирование на C#, путевые заметкиDotNetConf
 
Елена Жукова "Жизнь вне JavaScript"
Елена Жукова "Жизнь вне JavaScript"Елена Жукова "Жизнь вне JavaScript"
Елена Жукова "Жизнь вне JavaScript"Fwdays
 
Асинхронность и сопрограммы
Асинхронность и сопрограммыАсинхронность и сопрограммы
Асинхронность и сопрограммыPlatonov Sergey
 
Android - 01 - Java Basics
Android - 01 - Java BasicsAndroid - 01 - Java Basics
Android - 01 - Java BasicsNoveo
 
Unit test быстрый старт
Unit test быстрый стартUnit test быстрый старт
Unit test быстрый стартAntonio
 
Lambdas in java 8
Lambdas in java 8Lambdas in java 8
Lambdas in java 8chashnikov
 
Лекция 6
Лекция 6Лекция 6
Лекция 6itc73
 
RDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на SwiftRDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на SwiftRAMBLER&Co
 
C# Desktop. Занятие 12.
C# Desktop. Занятие 12.C# Desktop. Занятие 12.
C# Desktop. Занятие 12.Igor Shkulipa
 
Вечный вопрос измерения времени
Вечный вопрос измерения времениВечный вопрос измерения времени
Вечный вопрос измерения времениTatyanazaxarova
 
паттерны программирования
паттерны программированияпаттерны программирования
паттерны программированияguestfc8ae0
 

Semelhante a java (20)

8. java lecture threads
8. java lecture threads8. java lecture threads
8. java lecture threads
 
JPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerJPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profiler
 
Step 7
Step 7Step 7
Step 7
 
Svitla .Net meetup in Kiev, Anzhiiak Oleksii
Svitla .Net meetup in Kiev, Anzhiiak OleksiiSvitla .Net meetup in Kiev, Anzhiiak Oleksii
Svitla .Net meetup in Kiev, Anzhiiak Oleksii
 
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
 
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
 
Java весна 2014 лекция 5
Java весна 2014 лекция 5Java весна 2014 лекция 5
Java весна 2014 лекция 5
 
Programming Java - Lection 06 - Multithreading - Lavrentyev Fedor
Programming Java - Lection 06 - Multithreading - Lavrentyev FedorProgramming Java - Lection 06 - Multithreading - Lavrentyev Fedor
Programming Java - Lection 06 - Multithreading - Lavrentyev Fedor
 
Исключения и ошибки
Исключения и ошибкиИсключения и ошибки
Исключения и ошибки
 
Многопоточное программирование на C#, путевые заметки
Многопоточное программирование на C#, путевые заметкиМногопоточное программирование на C#, путевые заметки
Многопоточное программирование на C#, путевые заметки
 
Елена Жукова "Жизнь вне JavaScript"
Елена Жукова "Жизнь вне JavaScript"Елена Жукова "Жизнь вне JavaScript"
Елена Жукова "Жизнь вне JavaScript"
 
Асинхронность и сопрограммы
Асинхронность и сопрограммыАсинхронность и сопрограммы
Асинхронность и сопрограммы
 
Android - 01 - Java Basics
Android - 01 - Java BasicsAndroid - 01 - Java Basics
Android - 01 - Java Basics
 
Unit test быстрый старт
Unit test быстрый стартUnit test быстрый старт
Unit test быстрый старт
 
Lambdas in java 8
Lambdas in java 8Lambdas in java 8
Lambdas in java 8
 
Лекция 6
Лекция 6Лекция 6
Лекция 6
 
RDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на SwiftRDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на Swift
 
C# Desktop. Занятие 12.
C# Desktop. Занятие 12.C# Desktop. Занятие 12.
C# Desktop. Занятие 12.
 
Вечный вопрос измерения времени
Вечный вопрос измерения времениВечный вопрос измерения времени
Вечный вопрос измерения времени
 
паттерны программирования
паттерны программированияпаттерны программирования
паттерны программирования
 

java

  • 1. Лекция №10 «Threads» Автор: доц. каф. ПО ЭВМ ХНУРЭ Колесников Д.О. v.0.2
  • 2. 10.1. Выполнение инструкций потоками Поток выполнения – это последовательность команд, выполняемых процессором. Другие названия потока выполнения: поток вычисления, нить, (англ.) thread. Выполняемая программа может иметь несколько потоков. Если в программе выполняется некоторая инструкция (вызов метода, оператор, операция), то ее всегда выполняет некоторый поток.
  • 3. 10.2. Суперкласс потоков выполнения Любой класс поток является наследником класса java.lang.Thread. Выражение «на потоке вызван метод» следует понимать как вызов метода на объекте соответствующего класса потока. Выражение «поток выполняет метод» следует понимать как выполнение инструкций метода потоком.
  • 4. 10.3. Главный поток Любая программа имеет хотя бы один поток вычислений – главный поток. Точкой входа в главный поток является метод main. public class test { public static void main(String[] argv) { // инструкции метода main // выполняет главный поток программы } } Главный поток запускает JVM. Все инструкции метода main выполняет главный поток.
  • 5. 10.4. Статические методы класса Thread В любом месте программы можно вызывать статические методы класса Thread, которые относятся к текущему потоку, т.е. к тому потоку, который вызывает эти методы. Например, ссылку на объект Thread текущего потока можно получить с помощью статического метода Thread.currentThread();
  • 6. 10.5. Имя потока Любому потоку можно присвоить имя – либо с помощью конструктора, либо с помощью метода setName. Имя потока возвращает метод getName (оба метода setName и getName определены в классе Thread). public static void main(String[] argv) { Thread.currentThread().setName("main"); System.out.println( Thread.currentThread().getName()); // main } Замечание. JVM не использует имена потоков, они служат только для удобства программиста. Двум разным потокам можно присвоить одно и то же имя.
  • 7. 10.6. Создание и запуск дочернего потока (extends Thread) Чтобы создать поток нужно расширить класс Thread, перекрыв его метод run class B extends Thread { public void run() { // do something } }
  • 8. После создания поток можно запустить на выполнения с помощью метода start класса Thread. B b = new B(); b.start(); // или new B().start(); Замечание. Метод run является точкой входа в поток. Метод main является точкой входа в главный поток (его запускает JVM).
  • 9. 10.7. Создание и запуск дочернего потока (implements Runnable) Другой способ создания потока заключается в реализации интерфейса Runnable, который имеет один метод run. class B implements Runnable { public void run() { // do something } }
  • 10. После этого создают новый поток с помощью конструктора Thread(Runnable target). Thread t = new Thread(new B()); t.start(); // или new Thread(new B()).start(); Замечание. Класс Thread реализует интерфейс Runnable. public class Thread implements Runnable
  • 11. Замечание. В качестве параметра конструктору Thread может быть передан класс, расширяющий класс Thread. public class Test { public static void main(String[] argv) { Thread t = new Thread(new MyThread()); t.start(); } } class MyThread extends Thread { public void run() {} }
  • 12. 10.8. Создание и запуск потока в одном классе Поток можно создать и запустить в одном классе. public class MyThread extends Thread { public void run() { ... } public static void main(String[] argv) { // главный поток запускает новый поток new MyThread().start(); } }
  • 13. 10.9. Запуск потока в конструкторе класса-потока Часто поток запускают в конструкторе потока. class MyThread extends Thread { public MyThread() { this.start(); } public void run() { // do something } public static void main(String[] argv) { new MyThread (); } }
  • 14. 10.10. Создание и запуск потока с помощью анонимного класса Поток можно создать и запустить в методе с помощью анонимного класса. public class MyThread extends Thread { public static void main(String[] argv) { new Thread() { public void run() { // do something } }.start(); } }
  • 15. 10.11. Завершение выполнения потока Поток начинает свое выполнение, когда на нем вызовут метод start (в родительском потоке). Метод start в свою очередь вызывает метод run. Поток завершает свое выполнение после выполнения последней инструкции метода run. Аналогия для главного потока – главный поток завершает свое выполнение после выполнения последней инструкции метода main. Возможен выход из потоков в связи с выбросом исключений.
  • 16. Замечание. Существуют т.н. «потоки-демоны», которые предназначены для обслуживания других потоков. Если в программе запущенными остаются только потоки- демоны, то JVM принудительно прекращает их работу и завершает выполнение приложения. Чтобы сделать поток «демоном» необходимо перед его запуском вызвать на нем метод setDaemon с соответствующим значением.
  • 17. public class test extends Thread { public void run() { while(true); } // бесконечный цикл public static void main(String[] argv) throws InterruptedException { // главный поток создает поток test test t = new test(); t.setDaemon(true); // делает его «демоном» t.start(); // запускает // чтобы дочерний поток успел запуститься: Thread.sleep(1000); }// здесь главный поток завершает свое // выполнение и JVM завершает работу // приложения прерывая бесконечный цикл }
  • 18. 10.12. Метод sleep класса Thread Класс Thread содержит статический метод sleep, который делает паузу в выполнении текущего потока на заданное число миллисекунд. public static void sleep(long millis) throws InterruptedException
  • 19. Метод может выбросить исключение InterruptedException в том случае, если на потоке, для которого он делает паузу, будет вызван метод interrupt. public static void main(String[] argv) { for (int j = 0; j < 10; j++) { System.out.println(j); // главный поток переводится // в режим паузы примерно на 1 секунду try { Thread.sleep(1000); } catch (Exception e) {e.printStackTrace();} } }
  • 20. public class test extends Thread { public void run() { for (int j = 0; j < 10; j++) { System.out.println(j); try {Thread.sleep(200);} catch (Exception e) {e.printStackTrace();} } } public static void main(String[] args) throws InterruptedException { test t = new test(); t.start(); Thread.sleep(1000); t.interrupt(); // через секунду будет выброс // исключения методом sleep если он } // выполняется в данный момент }
  • 21. Замечание. Метод sleep перегружен. Точность времени паузы не гарантируется. // пауза на ms миллисекунд public static void sleep(long ms) // пауза на ms миллисекунд и ns наносекунд public static void sleep(long ms, int ns) Замечание. InterruptedException наследуется напрямую от Exception, поэтому необходимо предусмотреть обработку этого исключения.
  • 22. 10.13. Метод alive класса Thread Метод isAlive класса Thread возвращает true, если поток, на котором он вызван, запущен и еще не прекратил свое выполнение.
  • 23. public class test extends Thread { public static void main(String[] argv) { // гл. поток создает и запускает новый поток test t = new test(); t.start(); // главный поток вызывает метод // isAlive на запущенном потоке: while (boolean isAlive = t.isAlive()) { // будет выводится true пока // выполняется запущенный поток: System.out.println(isAlive); } // число циклов заранее неизвестно } // зависит от того, как долго будет // выполняться метод run public void run() {} }
  • 24. 10.14. Пример запуска двух потоков, которые выводят разные сообщения с разной периодичностью public class test extends Thread { private String mes; private int ms; // конструктор потока public test(String mes, int ms) { this.mes = mes; this.ms = ms; // запуск потока в конструкторе this.start(); }
  • 25. public void run() { while (true) { // бесконечный цикл // вывод сообщения System.out.println(this.mes); // пауза на ms миллисекунд try {sleep(this.ms);} catch (Exception e) {} } } // главный поток запускает три потока public static void main(String[] argv) { new test("A", 500); // первый new test("B", 700); // второй new test("C", 1000); // третий } }
  • 26. 10.15. Единственность способа запуска потока. Запуск потока возможен только с помощью метода start. Вызов метода run на объекте потоке приведет лишь к одноразовому выполнению тела этого метода в том потоке, который вызвал этот метод.
  • 27. public class test extends Thread { public void run() { Thread t = Thread.currentThread(); System.out.println(t.getName()); } public static void main(String[] argv) { // главному потоку задаем имя main Thread.currentThread().setName("main"); test t = new test(); // дочернему потоку задаем имя thread-0 t.setName("thread-0"); t.run(); // main t.start(); // thread-0 } }
  • 28. Замечание. JVM задает каждому потоку некоторое системное имя при его создании test t = new test(); Это имя возвращает метод getName, если его вызывать на соответствующем объекте-потоке. t.getName(); Если требуется получить имя потока, который выполняет некоторый метод, достаточно в код метода вставить следующую инструкцию: String threadName = Thread.currentThread().getName();
  • 29. 10.16. Проблема параллельного выполнения одного кода разными потоками Два разных потока могут выполнять один и тот же код, это может привести к нежелательным последствиям. public class test { private boolean flag; // выполняют два потока параллельно public void m(boolean flag) { this.flag = flag; try {Thread.sleep(200);} // ждем catch (InterruptedException e) {e.printStackTrace();} System.out.println(this.flag + " == " + flag); } // true == false
  • 30. public test() { // конструктор создает два потока new Thread() { // создание первого потока public void run() { while (true) m(true); } }.start(); // запуск первого потока new Thread() { // создание второго потока public void run() { while (true) m(false); } }.start(); // запуск второго потока } public static void main(String[] argv) { new test(); } }
  • 31. 10.17. Синхронизация Для разрешения проблемы параллельного доступа разных потоков к одному и тому же коду применяется синхронизация. Синхронизации может быть подвергнут метод public synchronized void m() {...} статический метод public static synchronized void m() {...} участок кода метода. public void m() { synchronized(o) {...} }
  • 32. Синхронизация осуществляется с помощью специального объекта-монитора, который связан с этой синхронизацией. Когда поток начинает выполнять синхронизированный код, говорят, что он блокирует соответствующий монитор (или владеет монитором). Если потоку нужно выполнить синхронизированный код, а монитор заблокирован другим потоком, то он ожидает, пока с монитора не будет снята блокировка.
  • 33. 10.18. Монитор синхронизации Монитором всегда является объект, который зависит от того, какой код синхронизируется. синхронизируемый монитор код нестатический метод объект this объект типа Class, статический метод соответствующий классу, в котором определен метод произвольный объект o - участок кода метода параметр инструкции synchronized
  • 34. Замечание. Монитором не может быть null. Замечание. Т.к. JVM создает уникальный объект типа Class, для каждого загруженного класса, то для статических синхронизированных методов существует один и только один монитор. Для нестатического синхронизированного метода может существовать несколько мониторов – по числу созданных объектов класса, в котором определен этот метод.
  • 35. public class test extends Thread { public synchronized void m() { while (true) System.out.println( Thread.currentThread().getName()); } public test() {this.start();} public void run() {this.m();} public static void main(String[] argv) { test treadA = new test(); // запуск потока A test threadB = new test(); // запуск потока B } // в процессе выполнения будут выводиться // имена потоков threadA и threadB вперемешку }
  • 36. В предыдущем примере два потока A и B выполняют один и тот же метод одновременно, т.к. они блокируют разные мониторы: threadA и threadB соответственно (синхронизация не работает). Если метод m сделать статическим, то на экран сообщения будет выводить только один поток – A. Второй поток B будет бесконечно долго ждать, пока первый не разблокирует монитор, который в данном случае один - объект класса Class, соответствующий классу test (test.class).
  • 37. 10.19. Инструкция synchronized Инструкция synchronized позволяет синхронизировать произвольный участок кода метода. public class test { // объект-монитор private Object lockA = new Object(); public void m() { synchronized (lockA) {... } } } Замечание. Параметром инструкции synchronized всегда является объект.
  • 38. 10.20. Метод join класса Thread Класс Thread содержит метод join предназначенный для перевода потока, который вызвал этот метод в режим паузы до тех пор, пока не закончит свою работу поток, на котором этот метод был вызван. Замечание. Метод может выбросить исключение InterruptedException, если на потоке, который находится в режиме паузы, будет вызван метод interrupt. Замечание. Поток, выполняющий метод join не снимает блокировки с мониторов, которые он заблокировал.
  • 39. class test extends Thread { public void run() { try {sleep(2000);} catch (Exception e) {e.printStackTrace();} } public static void main(String[] argv) throws Exception{ // создание и запуск дочернего потока: test t = new test(); t.start(); // главный поток ждет завершения // дочернего потока: t.join(); System.out.println("pause has expired"); } }
  • 40. 10.21. Метод wait класса Object Метод wait переводит поток в режим ожидания. Метод имеет три варианта. wait() бесконечное ожидание wait(long ms) ожидание ms миллисекунд wait(long ms, int ns) ожидание ms миллисекунд и ns наносекунд Замечание. wait(0) эквивалентно wait()
  • 41. Метод wait должен вызываться (1) только из синхронизированного кода (2) на объекте мониторе, связанным с данным синхронизированным кодом. (3) поток, который вызывает эти методы, должен владеть монитором. wait может выбросить исключение InterruptedException. Замечание. При вызове метода wait на мониторе, блокировка с монитора снимается и поток переходит в режим ожидания на этом мониторе. Замечание. Метод wait определен в классе Object. Монитором может быть любой объект.
  • 42. Замечание. Когда поток выполняет метод wait возможны т.н. случайные пробуждения. Поэтому выполнение метода wait следует заключать в цикл с проверкой условия. Object.java: * As in the one argument version, interrupts and spurious * wakeups are possible, and this method should always be * used in a loop: * synchronized (obj) { * while (<condition does not hold>) * obj.wait(timeout, nanos); * ... // Perform action appropriate to condition * }
  • 43. public class test extends Thread { // точка входа в поток: public void run() { // синхронизированный блок (монитор = this): synchronized (this) { // перевод this в режим ожидания: try {wait();} catch (Exception e) { // вывести сообщение после // прерывания: System.out.println( "thread has been interrupted"); } } }
  • 44. public static void main(String[] argv) throws Exception { // создание и запуск потока: test t = new test(); t.start(); // пауза в главном потоке, чтобы успел // начать свое выполнение метод wait: Thread.sleep(500); // прерывать запущенный поток: t.interrupt(); } }
  • 45. 10.22. Методы notify и notifyAll класса Object Для естественного выхода из режима ожидания применяются методы notify и notifyAll, которые должны: (1) вызываться на объекте-мониторе (2) только из синхронизированного кода. (3) поток, который вызывает эти методы, должен владеть монитором. На одном и том же мониторе может находиться несколько потоков в режиме ожидания. Метод notify пробуждает только один случайно выбранный поток; notifyAll пробуждает все потоки, которые ожидают на этом мониторе.
  • 46. Замечание. Методы notify и notifyAll определены в классе Object, т.к. монитором потенциально может являться любой объект. Замечание. Методы notify и notifyAll выбрасывают IllegalMonitorStateException с сообщением «current thread not owner», если поток, который вызывает эти методы, не является владельцем монитора. Замечание. Нежелательно использовать метод notify, т.к. неизвестно какой именно поток получит уведомление.
  • 47. public class test extends Thread { public void run() { // монитором является this /*1*/ synchronized (this) { try { // перевод в режим ожидания: wait(); // вывод сообщения при // пробуждении: System.out.println( "thread has been notified"); } catch (Exception e) { e.printStackTrace(); } } }
  • 48. public static void main(String[] argv) throws Exception { test t = new test(); t.start(); Thread.sleep(500); // главный поток входит в монитор t // (= this в строке /*1*/) synchronized (t) { // и выполняет вызов notify на мониторе // которым владеет главный поток t.notify(); } } }
  • 49. 10.23. Блокированное состояние пробужденного потока Если поток находился в состоянии ожидания, выполняя метод wait и был пробужден методом notify/notifyAll то он находиться в блокированном состоянии до тех пор, пока не освободиться монитор и только после этого продолжает выполнять первую инструкцию за методом wait.
  • 50. public class test extends Thread { public void run() { // "abc" – уникальный строковый литерал synchronized ("abc") { try { // поток вызывает на мониторе // "abc" метод wait "abc".wait(); // вывод на экран после // выполнения строки /*1*/ System.out.println( "thread has been notified"); } catch (Exception e) {e.printStackTrace();} } }
  • 51. public static void main(String[] argv) throws Exception { new test().start(); Thread.sleep(500); synchronized ("abc") { // главный поток вызывает на // мониторе "abc" метод notifyAll "abc".notifyAll(); // главный поток переводится в режим // паузы на примерно на 10 с. /*1*/ Thread.sleep(10000); } } }
  • 52. 10.24. Совместное использование методов wait и notify/notifyAll Совместное использование методов notify/notifyAll и wait дает возможность потокам синхронизировать свое выполнение. Замечание. Если на мониторе нет ожидающих потоков, то вызов методов notify/notifyAll не генерирует никаких исключительных ситуаций.
  • 53. public class test extends Thread { public static Object lock = new Object(); private String mes; public test(String mes) {this.mes = mes;} public void run() { synchronized (test.lock) { try { while (true) { test.lock.wait(); test.lock.notify(); System.out.println(this.mes); } } catch (Exception e) {e.printStackTrace();} } }
  • 54. public static void main(String[] argv) throws Exception { // после запуска оба потока переходят в // режим ожидания, выполняя wait new test("A").start(); new test("B").start(); // пауза, чтобы запущенные потоки успели // начать выполнение wait Thread.sleep(500); synchronized (test.lock) { // для оповещения всех ждущих на // мониторе потоков нужно вызвать на // нем метод notifyAll test.lock.notifyAll(); // } } }
  • 55. Замечание. Потоки могут синхронизировать свое выполнение без участия третьего (главного) потока. public class test extends Thread { private String mes; public test(String mes) {this.mes = mes;} public static void main(String[] argv) throws Exception { new test("A").start(); new test("B").start(); }
  • 56. public void run() { // монитор - объект класса Class, // соответствующий классу test synchronized (test.class) { try { while (true) { test.class.notify(); test.class.wait(); System.out.println(this.mes); } } catch (Exception e) {e.printStackTrace();} } } }
  • 57. 10.25. Взаимная блокировка Потоки могут входить в состояние взаимной блокировки.
  • 58. public class test extends Thread { public static Object lockA = new Object(); public static Object lockB = new Object(); public void run() { try { synchronized (test.lockA) { // после паузы поток ждет, пока // будет снята блокировка с // монитора test.lockB sleep(500); synchronized (test.lockB) {} } } catch (Exception e) {e.printStackTrace();} }
  • 59. public static void main(String[] argv) throws Exception { new test().start(); synchronized (test.lockB) { // после паузы поток ждет, пока будет // снята блокировка с монитора // test.lockA sleep(300); synchronized (test.lockA) {} } } }
  • 60. 10.26. Состояния потоков Различают следующие состояния потока: 1) созданный; 2) запущенный; 3) блокированный; 4) остановленный. Замечание. Не существует средств различения состояний «поток запущен» и «поток блокирован».
  • 61. 10.27. Случаи блокирования и разблокирования потоков Блокирование и разблокирование потока осуществляется в следующих случаях.
  • 62. причина блокирования потока: поток вызывает метод sleep момент разблокирования потока: 1) истечение времени паузы 2) на этом потоке вызван метод interrupt при блокировке потока блокировка с монитора, которым он владеет: не снимается
  • 63. причина блокирования потока: поток начинает выполнять операцию ввода/вывода момент разблокирования потока: естественное завершение операций ввода/вывода при блокировке потока блокировка с монитора, которым он владеет: не снимается
  • 64. причина блокирования потока: попытка заблокировать монитор, который уже заблокирован момент разблокирования потока: снятие блокировки с монитора при блокировке потока блокировка с монитора, которым он владеет: не снимается
  • 65. причина блокирования потока: поток вызывает метод join момент разблокирования потока: 1) поток, на котором вызван метод join, завершает свое выполнение 2) на этом потоке вызван метод interrupt при блокировке потока блокировка с монитора, которым он владеет: не снимается
  • 66. причина блокирования потока: поток начинает выполнять метод wait и переходит в режим ожидания на этом мониторе момент разблокирования потока: 1) истечение времени ожидания 2) на этом потоке вызван метод interrupt 3) на мониторе вызван метод notify/notifyAll 4) случайное пробуждение при блокировке потока блокировка с монитора, которым он владеет: снимается
  • 67. 10.28. Метод interrupt класса Thread Если поток находится в блокированном состоянии в результате выполнения методов sleep, join или wait, а другой поток вызывает на этом потоке метод interrupt, то соответствующие методы прекращают свое выполнение и выбрасывают исключение InterruptedException.
  • 68. public class test extends Thread { public void run() { try { sleep(10000); // InterruptedException this.join(); // InterruptedException synchronized (test.class) { // InterruptedException: test.class.wait(); } } catch (Exception e) { e.printStackTrace(); } }
  • 69. public static void main(String[] argv) throws Exception { test t = new test(); t.start(); t.interrupt(); } }
  • 70. 10.29. Методы interrupted и isInterrupted класса Thread Потоки имеют внутренний флаг, который определяет, был ли прерван поток методом interrupt. Для проверки этого флага применяются методы interrupted и isInterrupted. public static boolean interrupted() проверяет был ли текущий поток прерван и сбрасывает внутренний флаг в false. public boolean isInterrupted() проверяет был ли поток (на котором данный метод вызван) прерван, внутренний флаг при этом остается без изменений.
  • 71. Замечание. Методы wait, sleep и join выбросив исключение InterruptedException сбрасывают флаг прерывания потока в false. class test { public static void main(String[] argv) { Thread t = Thread.currentThread(); t.interrupt(); System.out.println(t.isInterrupted()); // true System.out.println(t.isInterrupted()); // true System.out.println(Thread.interrupted()); // true System.out.println(Thread.interrupted()); // false } }
  • 72. 10.30. Изменение значения аргумента блока синхронизации Изменение значения аргумента синхронизированного блока не приводит к смене монитора в нем (имеется ввиду присваивание аргументу значения нового объекта). Поэтому если в качестве аргумента нескольких синхронизированных блоков предполагается использовать специально созданное для этих целей поле, то целесообразно объявлять его с модификатором final.
  • 73. Пример изменения аргумента синхронизированного блока: public class test extends Thread { Object monitor = "123"; public void m() throws Exception { synchronized (monitor) { System.out.println( "montior1 = " + monitor); monitor = "abc"; Thread.sleep(5000); System.out.println( "monitor2 = " + monitor); } }
  • 74. public void run() { try {m();} catch (Exception E) {} } public static void main(String[] argv) throws Exception { test t = new test(); t.start(); Thread.sleep(1000); synchronized ("123") { System.out.println( monitor3 = " + t.monitor); } } }
  • 75. Программа выведет в стандартный поток вывода следующие строки: montior1 = 123 monitor2 = abc monitor3 = abc т.е., блокировка с объекта "123" не снимается.