Три минуты, чтобы начать работу с технологией многопоточного управления Java

Java

Очень важным отличием java от python, php и других скриптовых языков является то, что java имеет настоящую технологию управления многопоточностью, освоение технологии многопоточности очень важно для использования языка java

Поведение по умолчанию

  • Основной поток будет ждать завершения выполнения всех дочерних потоков перед выходом.
  • Неупорядоченное выполнение нескольких потоков

создать тему

Реализуйте интерфейс Runnable, поместите код, выполняемый в потоке, в реализацию интерфейса Runnable и дайте классу реализации имя, например MyRunner:

public class MyRunner implements Runnable {
    @Override
    public void run() {
      // 在线程中执行的代码
      System.out.println("running in a thread, name: " + Thread.currentThread().getName());
    }
}

Создать объект потокаjava.lang.Threadи используйте MyRunner в качестве тела выполнения потока, затем вызовитеThread.start()метод запуска потока

public class MyThread {
    public static void main(String[] args) {
      new Thread(new MyRunner()).start();
      new Thread(new MyRunner()).start();
      new Thread(new MyRunner()).start();
      new Thread(new MyRunner()).start();

      System.out.println("Main thread exit");
    }
}

Эффект от исполнения следующий

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

приостановить нить

можно использоватьThread.sleep()Метод приостанавливает выполнение потока, например приостанавливает на 5 секунд.

  try {
    Thread.sleep(5000);
  } catch (InterruptedException e) {
    e.printStackTrace();
  }

Видно, что после того, как поток вызовет Thread.sleep для перехода в состояние приостановки, в процессе приостановки может возникнуть исключение, которое указывает на то, что он получил сигнал о выходе из состояния приостановки.

Завершает приостановленное состояние потока

Вызывающий объект потока (java.lang.Thread)interrupt()Метод может завершить поток в приостановленном состоянии и заставить его возобновить состояние выполнения.

Тело выполнения потока MyRunner.java:

public class MyRunner implements Runnable {
    @Override
    public void run() {
      try {
        Thread.sleep(5000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      // 在线程中执行的代码
      System.out.println("running in a thread, name: " + Thread.currentThread().getName());
    }
}

Вызовите метод прерывания потока, чтобы завершить состояние сна потока MyThread.java

public class MyThread {
    public static void main(String[] args) {
      Thread thread = new Thread(new MyRunner());
      thread.start();
      thread.interrupt();

      System.out.println("Main thread exit");
    }
}

Эффект от исполнения следующий:

Вы можете видеть, что сон потока был завершен, и было получено InterruptedException.

можно использоватьThread.currentThread().isInterrupted()Вручную проверьте, не был ли прерван текущий поток

Активно ждать завершения выполнения дочернего потока

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

MyThread.java имитирует поток загрузки файлов

public class MyRunner implements Runnable {
    @Override
    public void run() {
      System.out.println("1. start download file...");
      try {
        Thread.sleep(5000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println("2. download file complete");
    }
}

Основной поток использует метод join() для ожидания завершения выполнения потока.

Thread thread = new Thread(new MyRunner());
thread.start();

try {
  thread.join();
} catch (InterruptedException e) {
  e.printStackTrace();
}
System.out.println("3. process downloaded file");

Эффект от исполнения следующий:

Технология уведомления о статусе между потоками wait() и notify()

Тема 1: Скачать файл Тема 2: после завершения загрузки файла обработайте файл

DownloadStatus.java передает статус загрузки файла между потоками

public class DownloadStatus {
  private boolean isFinish = false;

  public boolean isFinish() {
    return isFinish;
  }

  public void setFinish(boolean finish) {
    isFinish = finish;
  }
}

Скачать файл thread DownloadThread.java

public class DownloadThread implements Runnable {

  private DownloadStatus status;

  public DownloadThread(DownloadStatus status) {
    this.status = status;
  }

  @Override
  public void run() {
    try {
      Thread.sleep(5000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("file in thread download finished.");
    status.setFinish(true);
    synchronized (status) {
      status.notifyAll();
    }
  }
}

Видно, что после завершения загрузки файла флаг завершения загрузки isFinish устанавливается в значение true и вызывается метод notifyAll() объекта статуса.

Обработка файла потоков

DownloadStatus status = new DownloadStatus();

Thread thread = new Thread(new DownloadThread(status));
thread.start();
new Thread(() -> {
  while (!status.isFinish()) {
    synchronized (status) {
      try {
        status.wait();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
  System.out.println("process download file");
}).start();

Запустите бесконечный цикл в файловом потоке, чтобы проверить, загружен ли файл во время !status.isFinish(), и вызовите метод wait() в теле цикла, чтобы дождаться уведомления об объекте статуса загрузки.

Использование notifyAll() и wait() для уведомления об изменении состояния позволяет избежать бесконечного цикла, занимающего слишком много ресурсов ЦП.

Технология многопоточности с гарантированной переменной безопасностью

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

1. Каждый поток использует независимую переменную, и после выполнения всех потоков суммируйте переменные в каждом потоке. 2. Блокируйте переменные и добавляйте повторные блокировки

lock = new ReentrantLock();
lock.lock();
// 对变量进行修改的code
lock.unlock();

3. Используйте синхронизированное ключевое слово

synchronized(需要检测的对象) {
    // 对变量进行修改的code
}

4. Волатильное Ключевое слово: заставить процессор не размещать переменные в память в кэш CPU, избегайте проблем с многоподобной переменной видимостью

private volatile bool isFinished;

5. класс атомарной серии, java.util.concurrent.atomic

private AtomicInteger count;

некоторые замечания

  1. notify уведомляет поток ожидания(), notifyAll уведомляет все потоки ожидания
  2. Многопоточные объекты коллекций могут использовать синхронизированные коллекции для обеспечения безопасности потоков.Collection<Integer> intList = Collections.synchronizedCollection(new ArrayList<>())
  3. Объекты многопоточных хеш-таблиц могут использовать ConcurrentHashMap для обеспечения безопасности потоков.Map<Integer, String> map = new ConcurrentHashMap<>()

использованная литература

  1. Блог Woohoo.cn на.com/краткосрочный заработок/fear/50…
  2. blog.CSDN.net/only bless/ ах…
  3. woo woo .cn blog on.com/ignorant/Arch…