1. Что такое многопоточность?
Цитирую кого-то из интернета:
- Один процесс, один поток: один человек ест за одним столом.
- Однопроцессорная многопоточность: несколько человек едят вместе за одним столом.
- Многопроцессорный однопоточный: несколько человек едят за своим столом.
Проблема многопоточности заключается в том, что когда несколько человек одновременно едят блюдо, легко конкурировать, например, два человека одновременно держат блюдо. Конфликт и соперничество возникнут, когда произойдет совместное использование ресурсов.
Преимущества использования многопоточности (по сравнению с использованием многопроцессорности):
- Память нельзя разделить между процессами, но очень легко разделить память между потоками.
- Ресурсы, выделяемые системой для создания потока, относительно дешевы по сравнению с процессом создания.
2. Введение и сравнение трех методов реализации многопоточности в Java
- Наследовать от класса Thread
- Реализовать интерфейс Runnable
- Реализовать вызываемый интерфейс
Введение и сравнение трех методов
1. Реализация интерфейса Runnable имеет следующие преимущества перед наследованием класса Thread.
1) Можно избежать ограничений из-за функции одиночного наследования Java.
2) Повышение надежности программы, код может совместно использоваться несколькими потоками, а код и данные независимы.
3) Подходит для нескольких потоков одного и того же программного кода для обработки одного и того же ресурса.
4) Пул потоков можно размещать только в потоках, реализующих Runable или Callable, и нельзя размещать непосредственно в классах, наследующих Thread.
2. Разница между реализацией интерфейса Runnable и реализацией интерфейса Callable
1) Runnable доступен начиная с java1.1, а Callable добавлен после 1.5
2) Поток задачи, реализующий интерфейс Callable, может вернуть результат выполнения, тогда как поток задачи, реализующий интерфейс Runnable, не может вернуть результат.
3) Метод call() интерфейса Callable позволяет генерировать исключения, в то время как исключения метода run() интерфейса Runnable могут быть обработаны только внутренне и не могут продолжать генерироваться.
4) Присоединитесь к пулу потоков для запуска, Runnable использует метод выполнения ExecutorService, а Callable использует метод отправки.
Примечание: интерфейс Callable поддерживает возврат результата выполнения, в этом случае необходимо вызвать метод FutureTask.get(), который блокирует основной поток до получения возвращаемого результата, когда этот метод не вызывается, основной поток не блокируется.
3. Случаи Runnable, Thread и Callable
3.1 Первый способ реализации — наследование класса Thread
Наследуя класс Thread, вам необходимо переопределить метод run().При создании подкласса класса Thread вам необходимо переопределить run() и добавить код, который будет выполняться потоком.
package cn.huangt.java_learn_notes.multithread;
/**
* 继承Thread实现多线程
* @author huangtao
*/
public class ThreadExtends {
public static void main(String[] args) {
new MyThread("Thread测试").start();
new MyThread("Thread测试").start();
}
}
class MyThread extends Thread{
private String acceptStr;
public MyThread(String acceptStr) {
this.acceptStr = acceptStr;
}
public void run() {
for (int i = 0; i < 5; i ++) {
System.out.println("这个传给我的值:"+acceptStr+",加上一个变量,看看是什么效果:"+i);
}
}
}
/*
输出内容===
这个传给我的值:Thread测试,加上一个变量,看看是什么效果:0
这个传给我的值:Thread测试,加上一个变量,看看是什么效果:0
这个传给我的值:Thread测试,加上一个变量,看看是什么效果:1
这个传给我的值:Thread测试,加上一个变量,看看是什么效果:2
这个传给我的值:Thread测试,加上一个变量,看看是什么效果:3
这个传给我的值:Thread测试,加上一个变量,看看是什么效果:4
这个传给我的值:Thread测试,加上一个变量,看看是什么效果:1
这个传给我的值:Thread测试,加上一个变量,看看是什么效果:2
这个传给我的值:Thread测试,加上一个变量,看看是什么效果:3
这个传给我的值:Thread测试,加上一个变量,看看是什么效果:4
*/
3.2 Второй способ реализации — реализация интерфейса Runnable
Если вы хотите добиться множественного наследования, вы должны использовать реализации.Java предоставляет интерфейс java.lang.Runnable для решения вышеуказанных проблем.
Runnable могут обмениваться данными. Несколько потоков могут загружать Runnable одновременно. Когда каждый поток получает квант времени ЦП, Runnable начинает работать. Ресурсы в Runnable являются общими, поэтому использование Runnable более гибко. PS: Необходимо решить проблему конкуренции ресурсов после обмена.
package cn.huangt.java_learn_notes.multithread;
/**
* Runnable接口实现多线程
* @author huangtao
*/
public class RunnableImpl implements Runnable {
private String acceptStr;
public RunnableImpl(String acceptStr) {
this.acceptStr = acceptStr;
}
public void run() {
try {
// 线程阻塞1秒,此时有异常产生,只能在方法内部消化,无法上抛
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 最终处理结果无法返回
System.out.println("hello : " + this.acceptStr);
}
public static void main(String[] args) {
Runnable runnable = new RunnableImpl("Runable测试");
long beginTime = System.currentTimeMillis();
new Thread(runnable).start();
long endTime = System.currentTimeMillis();
// endTime 和 beginTime是一样的,线程并不会阻塞主线程
System.out.println("cast : " + (endTime - beginTime) / 1000 + " second!");
}
}
/*
输出内容===
cast : 0 second!
hello : Runable测试
*/
3.3, третья - реализовать интерфейс Callable
Runnable — это независимая задача, которая выполняет работу, но не возвращает никакого значения. Если вы хотите, чтобы задача возвращала значение после завершения, вы можете реализовать интерфейс Callable вместо интерфейса Runnable. Представленный в Java SE5, Callable — это универсальный тип с параметрами типа, представляющими значение, возвращаемое из метода call() (не run()).
package cn.huangt.java_learn_notes.multithread;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
/**
* Callable接口实现多线程
* @author huangtao
*/
public class CallableImpl implements Callable<String> {
private String acceptStr;
public CallableImpl(String acceptStr) {
this.acceptStr = acceptStr;
}
public String call() throws Exception {
// 任务阻塞1秒,并且增加一些信息返回
Thread.sleep(1000);
return this.acceptStr + " 增加一些字符并返回";
}
public static void main(String[] args) throws Exception {
Callable<String> callable = new CallableImpl("Callable测试");
FutureTask<String> task = new FutureTask<String>(callable);
// 创建线程
new Thread(task).start();
long beginTime = System.currentTimeMillis();
// 调用get()阻塞主线程,反之,线程不会阻塞
String result = task.get();
long endTime = System.currentTimeMillis();
System.out.println("hello : " + result);
// endTime 和 beginTime是不一样的,因为阻塞了主线程
System.out.println("cast : " + (endTime - beginTime) / 1000 + " second!");
}
}
/*
输出内容===
hello : Callable测试 增加一些字符并返回
cast : 1 second!
*/
4. Резюме Runnable, Thread и Callable
Наконец, давайте взглянем на резюме между тремя из них.
4.1 Реализация интерфейса Runnable имеет следующие преимущества перед наследованием класса Thread
1) Можно избежать ограничений из-за функции одиночного наследования Java.
2) Повышение надежности программы, код может совместно использоваться несколькими потоками, а код и данные независимы.
3) Подходит для нескольких потоков одного и того же программного кода для обработки одного и того же ресурса.
4) Пул потоков можно размещать только в потоках, реализующих Runable или Callable, и нельзя размещать непосредственно в классах, наследующих Thread.
4.2 Разница между реализацией интерфейса Runnable и реализацией интерфейса Callable
1) Runnable доступен начиная с java1.1, а Callable добавлен после 1.5
2) Поток задачи, реализующий интерфейс Callable, может вернуть результат выполнения, тогда как поток задачи, реализующий интерфейс Runnable, не может вернуть результат.
3) Метод call() интерфейса Callable позволяет генерировать исключения, в то время как исключения метода run() интерфейса Runnable могут быть обработаны только внутренне и не могут продолжать генерироваться.
4) Присоединитесь к пулу потоков для запуска, Runnable использует метод выполнения ExecutorService, а Callable использует метод отправки.
Примечание: интерфейс Callable поддерживает возврат результата выполнения, в этом случае необходимо вызвать метод FutureTask.get(), который блокирует основной поток до получения возвращаемого результата, когда этот метод не вызывается, основной поток не блокируется.
5. Другие
Конечно, когда дело доходит до многопоточности, просто освоить их определенно недостаточно.
Существует также принцип реализации многопоточности, а также глубокое понимание пула потоков Java, чтобы лучше использовать многопоточность.
Я обновлю в более поздней статье.
Код из статьи есть у меня на GitHub:GitHub.com/huangtao120…
Мой блог будет перемещен и синхронизирован с сообществом Tencent Cloud+, и я приглашаю всех присоединиться:cloud.Tencent.com/developer/ это…