3.1 Разработка игрового веб-сервера на Java. Примеры работы с потоками в Java


src/main/Main.java
package main;

public class Main {
    public static void main(String[] args) throws InterruptedException{
        //RandomRunExample.example();
        SeriesRunExample.example();
        //ThreadInterference.example();
    }
}


В классе RandomRunExample создаются 10 потоков и запускаются. В каждом потоке запускается метод run() этого класса. Демонстрируется непоследовательная работа потоков.

src/main/RandomRunExample.java
package main;

import java.util.logging.Logger;

public class RandomRunExample extends Thread {
    //private static Logger log = Logger.getLogger("ClassName");
    
    public void run(){
        System.out.println(Thread.currentThread().getName());
        //log.info(Thread.currentThread().getName());
    }
    
    public static void example(){
        for(int i=0; i<10; ++i){
            Thread thread = new RandomRunExample();
            thread.start();
        }
    }
}


В классе SeriesRunExample демонстрируется последовательная работа потоков с помощью wait() и notify(). Создаются десять потоков, каждому передается его номер и waitObject. Далее сравнивая свой номер mainId с текущим номером currentMax каждый поток проверяет настала ли его очередь выполнять свои действия. Если нет, то переходит в режим ожидания. Если да, то выполняет свои действия, увеличивает текущий номер currentMax и уведомляет всех кто находится в режиме ожидания о том что можно выйти из режима ожидания.

wait(), notify() и notifyAll() - методы класса Object:
  • object.wait() - ждать в текущем потоке, пока не прийдет notify();
  • object.notify() - сигнал "продолжить" первому кто начал wait();
  • object.notifyAll() - сигнал "продолжить" всем кто начал wait().

src/main/SeriesRunExample.java
package main;

import java.util.logging.Logger;

public class SeriesRunExample extends Thread {
    private static Logger log = Logger.getLogger(Main.class.toString());
    private static int currentMax = 1;
    private int mainId;
    private Object waitObject;
    
    public SeriesRunExample(int id, Object waitObject){this.mainId = id; this.waitObject = waitObject;}
    
    public static void example(){
        Object waitObject = new Object();
        for(int i = currentMax; i <= 10; ++i){
            Thread thread = new SeriesRunExample(i, waitObject);
            thread.start();
        }
    }
    
    public void run(){
        try {
            synchronized (waitObject) {
                while(mainId > currentMax){
                    waitObject.wait();
                }
                currentMax++;
                System.out.println("Hello from thread: " + mainId);
                //log.info("Hello from thread: " + mainId);
                waitObject.notifyAll();

            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}


В классе ThreadInterference демонстрируются решение проблемы интерференции (взаимные помехи) потоков с помощью lock-объекта. Примитивы для работы в многопоточной среде. Оба потока по очереди захватывают lock и инкрементируют переменные i и counter. В данном примере два потока делают то же, что было бы если бы вместо двух потоков работал один. Нужно понимать что synchronized не обеспечивает параллельный доступ, synchronized гарантирует, что доступ будет предоставлен только одному потоку в любой момент времени, а другой поток будет ждать свою очередь. После окончания работы оба потока выведут "i: 100000001".

src/main/ThreadInterference.java
package main;

public final class ThreadInterference extends Thread {
  private static final int HundredMillion = 100000000;
  private static int counter = 0;
  private static int i = 0;
  private static Object lock = new Object();

  public static void example() throws InterruptedException {
    (new ThreadInterference()).start();
    (new ThreadInterference()).start();   
  }

  public void run() {
    while (counter < HundredMillion) {
      synchronized (lock) {        
        counter++;
        i++;
      }
    }
    System.out.println(" i: " + i);
  }

}

В классе UnAtomic метод increaseCounter() синхронно вызывается двумя потоками. Оба потока поочередно работают над одной и той же задачей - увеличением счетчиков i и counter до ста миллионов. Пока идет работа метод increaseCounter() возвращает true, как работа будет закончена метод будет возвращать false. Таким образом получается что счетчик j должен был бы отражать количество сделанных инкрементов, но так как увеличение переменной j происходит одновременно и не синхронно из обоих потоков, то получается неверное значение (проблема гонки данных). По окончанию работы оба потока выведут: "Value of j: 99976788 Value of i: 100000000".

src/main/UnAtomic.java
package main;

public class UnAtomic extends Thread{
    private static int counter=0;
    private static int j=0;
    private static int i=0;
    
    public static void main(String[] args) throws InterruptedException{
        (new UnAtomic()).start();
        (new UnAtomic()).start();
    }
    
    private static synchronized boolean increaseCounter(){
        if(counter < 100000000) {
            counter++; 
            i++;
            return true; 
        }
        return false;
    }
    
    public void run(){
        while(increaseCounter()){
            j++; 
        }
        System.out.append("Value of j: " + j + " Value of i: " + i + "\n");
    }   
}

--
https://github.com/vitaly-chibrikov/tp_java_2013_02/tree/master/lecture2