Руководство по Java Core. Многопоточность.

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

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

Суть этого механизма заключается в том, что мы создаём два и более потока, которые работают параллельно.


Жизненный цикл потока

Давайте подробно рассмортим жизненный цикл потока.

  • Новый (New)
    Созданный поток назодится  в состоянии новый и назодится в нём до начала его работы.
  • Исполняемый (Runnable)
    После того, как поток начал свою работу он становится исполняемым (runnable). Это означает, что данный поток выполняет какую-то работу.
  • Ожидающий (Waiting)
    Временами поток не может выполнять свою работу, например ожидает другой поток, который выполняет определённые вычисления. Поток находится в состоянии ожидания до момента, когда получит опредлённый сигнал, например о том, что все необходимые вычисления выолнены.
  • Временно ожидающий (Timed waiting)
    Мы можем задать потоку определённое время ожидания, по истечении которого он продолжит свою работу.
  • Остановленный (Terminated)
    После того, как поток выполнит свою работу, он преходит в состояние остановленный (прекращённый).

Свойства потока

Каждый поток имеет опредлённый приоритет, который помогает операционной системе (далее – ОС) опредлять, в каком именно порядке имеющиеся потоки должны юыть запущены.

Приоритет потоков варьируется от миниального (MIN_PRIORITY – 1), до максимального (MAX_PRIORITY – 10). По умолчанию, все потоки создаются с нормальным приоритетом (NORM_PRIORITY – 5) .

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


Создание потока с помощью интерфейса Runnable

Для понимания того, как это работает на практике рассмотрим пример простого приложения.

Стоит отметить, что класс, в котором мы хотим использовать многопоточность, должен имплементировать интерфейс Runnable.

Пример:

Класс ThreadByRunnable


public class ThreadByRunnable implements Runnable {
    private Thread thread;
    private String threadName;

    public ThreadByRunnable(String threadName) {
        this.threadName = threadName;
        System.out.println("Thread " + threadName + " created successfully.");
    }

    @Override
    public void run() {
        System.out.println("Thread " + threadName + " is running...");

        try {


            for (int i = 1; i <= 5; i++) {
                System.out.println("Thread " + threadName + " " + i);
                Thread.sleep(100);
            }
        }catch (InterruptedException e){
            System.out.println("Thread " + threadName + " interrupted.");
            e.printStackTrace();
        }

        System.out.println("Leaving thread " + threadName);
    }

    public void start() {
        System.out.println("Thread " + threadName + " is started successfully.");
        if(thread == null){
         thread = new Thread(this, threadName);
            thread.start();
        }
    }
}

Класс MultithreadingDemo


public class MultithreadingDemo {
    public static void main(String[] args) {
        ThreadByRunnable threadOne = new ThreadByRunnable("ThreadOne");
        ThreadByRunnable threadTwo = new ThreadByRunnable("ThreadTwo");

        threadOne.start();
        threadTwo.start();
    }
}

В результате работы программы, мы получим, примерно, следующий результат:


/*Some System Messages*/

Thread ThreadOne created successfully.
Thread ThreadTwo created successfully.
Thread ThreadOne is started successfully.
Thread ThreadTwo is started successfully.
Thread ThreadOne is running...
Thread ThreadOne 1
Thread ThreadTwo is running...
Thread ThreadTwo 1
Thread ThreadOne 2
Thread ThreadTwo 2
Thread ThreadOne 3
Thread ThreadTwo 3
Thread ThreadOne 4
Thread ThreadTwo 4
Thread ThreadOne 5
Thread ThreadTwo 5
Leaving thread ThreadOne
Leaving thread ThreadTwo


Создание потока с помощью класса Thread

Второй способ создания потока – наследование класса Thread. Этот способ даёт нам больше гибкости при работе с потоками, благодаря методам класса Thread.

Рассмотрим пример простого приложения.

Пример:

Класс ThreadByExtending


public class ThreadByExtending extends Thread{
    private Thread thread;
    private String threadName;

    public ThreadByExtending(String threadName) {
        this.threadName = threadName;
        System.out.println("Thread " + threadName + " created successfully.");
    }

    public void run(){
        System.out.println("Thread " +  threadName + " is running...");

        try {
            for(int i = 1;i<=5; i++){
                System.out.println("Thread " + threadName + " " + i);
                Thread.sleep(100);
            }
        }catch (InterruptedException e){
            System.out.println("Thread " + threadName + " is interrupted.");
            e.printStackTrace();
        }
        System.out.println("Leaving thread " + threadName);
    }

    public void start(){
        System.out.println("Thread " + threadName + " is started successfully.");
        if(thread == null){
            thread = new Thread(this,threadName);
            thread.start();
        }
    }
}

Класс ThreadByExtendingDemo


public class ThreadByExtendingDemo {
    public static void main(String[] args) {
        ThreadByExtending threadOne = new ThreadByExtending("Thread One");
        ThreadByExtending threadTwo = new ThreadByExtending("Thread Two");

        threadOne.start();
        threadTwo.start();
    }
}

В результате работы программы, мы получим, примерно, следующий результат:


/*Some System Messages*/

Thread Thread One created successfully.
Thread Thread Two created successfully.
Thread Thread One is started successfully.
Thread Thread Two is started successfully.
Thread Thread One is running...
Thread Thread One 1
Thread Thread Two is running...
Thread Thread Two 1
Thread Thread One 2
Thread Thread Two 2
Thread Thread One 3
Thread Thread Two 3
Thread Thread One 4
Thread Thread Two 4
Thread Thread One 5
Thread Thread Two 5
Leaving thread Thread One
Leaving thread Thread Two

С конструкторами и методами класса Thread вы можете ознакомиться в официальной документации.


Синхронизация потока

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

Например, несколько потоком пытаются внести изменения в один  и тот же текстовый файл. В результате этого данные могут быть повреждены, так как один поток пытается записать данные “поверх” данных второго потока.

Для предотвращения таких ситуаций приеняется синзронизация.

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

Для понимания того, как то работает на практике, рассмотрим пример простого приложения.

ССЫЛКА НА ПРИМЕР.


Основные операции с потоками

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

Рассмотрим пример простого приложения.

ССЫЛКА НА ПРИМЕР.


Взаимодейтсвие потоков

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

Для обеспечения этого процесса применяются такие методы, как

  • public void wait()
    Этот метод  метод переводит поток в режим ожидания до момента вызова метода notify().
  • public void notify()
    Этот метод переводит один поток из режима ожидания в активный режим.
  • public void notifyAll()
    Этот метод переводит все потоки, которые находятся в режиме ожидания в активный режим.

Все эти методы реализованы, как финальные методы класса Object, что означает их доступность для любого класса в языке программирования Java. Все эти методы могут быть применены только в контексте синхронизации.

Для понимания того, как это работает на практике, рассмотрим пример простого приложения.

ССЫЛКА НА ПРИМЕР.


Взаимная блокировка потоков

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

Для понимания того, как это работает на практике рассмотрим пример простого приложения.

ССЫЛКА НАПРИМЕР.


В этом уроке мы изучили основы многопоточности в языке программирования Java и рассмотрели простые примеры с её применением.

В следующем уроке мы изучим апплеты в языке программирования Java.