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