Наиболее часто ругаемое место на собеседованиях должно иметь знание параллельного программирования, будь вы новичок, который только начинает или CRUD-фрик с 2-3 годами опыта, а это значит, что такой вопрос вам зададут на минимум 3 года., почему бы не потратить время на борьбу до конца. Лучший способ избавиться от страха — встретиться с ним лицом к лицу, Олли! (Эта серия представляет собой заметки и резюме моего учебного процесса, а также содержит отладочный код, в который может играть каждый)
Резюме этого раздела
Изучив этот раздел, вы будете иметь четкое представление о том, что такое параллельное программирование и каковы основные проблемы параллельного программирования. И может выполнить три метода реализации потоков самостоятельно. (Студенты, знакомые с этой темой, могут поставить лайк 👍, чтобы завершить эту главу)
1. В чем основная проблема параллельного программирования?
Я не знаю, сталкивались ли вы с той же проблемой, что и я. Прежде чем вы действительно используете эти знания для развития, я задам вопросы, которые вы часто задавали в последние два года.Двойные одиннадцать ящиковСочетая два, я чувствуюдвойной одиннадцатьВысокий уровень параллелизмапараллельное программированиепродукт. Вот что я хочу сказать,двойной одиннадцатьВысокий параллелизм случая должен включатьпараллельное программированиесодержание, нопараллельное программированиеНедостаточно решить сценарий двойного 11. Если вы хотите изучить решение двойного 11, вы можете изучить такие точки знаний, как асинхронная очередь и кеш.
из-за компьютераCUP-память-дискРазница в скорости между тремя велика, что приводит к недостаточному использованию аппаратных ресурсов, поэтому高并发编程要解决的问题是最大效率的利用硬件资源,从而达到程序效率提升的效果
.
2. Три способа «создать» тред
Почему я беру здесь слово "создать" в кавычках?
фокус
В общем, мы скажем, что существует три способа создания потока: один — создать поток, один — реализовать интерфейс Runnable, а другой — использовать Futuretask. Однако это утверждение не является строгим.После изучения этого случая я считаю, что каждый может естественным образом прийти к выводу.创建线程只有一种方式那就是构造Thread类
.
(1) Резьба
Унаследовав класс Thread и переопределив метод run(). Без лишних слов переходим непосредственно к коду.
public static class ThreadFirstTime extends Thread {
@Override
public void run() {
while (true) {
System.out.println(this.getName() + "create Thread success!");
}
}
}
public static void main(String[] args) {
//第一种写法通过new ThreadFirstTime()来创建对象
ThreadFirstTime threadFirstTime = new ThreadFirstTime();
//通过Thread.start()来启动线程
threadFirstTime.start();
//第二种 通过把new ThreadFirstTime()对象当作参数传入Thread的有参构造器中
Thread thread = new Thread(new ThreadFirstTime());
thread.start();
}
Вы можете видеть, что через новый объект Thread для вызова собственногоstart
метод, наш поток работает
вывод:
Thread-0create Thread success!
Thread-0create Thread success!
Thread-0create Thread success!
Обратите внимание здесьstart
метод, мы дополнительно проанализируем и обсудим этот метод позже.
(2) Работающий
Путем реализации интерфейса Runnable и реализацииrun()
метод реализации потоков. Код очень простой, перейдите непосредственно к
public static class RunnableFirstTime implements Runnable {
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() + "create Runnable success!");
}
}
}
public static void main(String[] args) {
//创建一个Thread线程,使用RunnableFirstTime
Thread thread = new Thread(new RunnableFirstTime());
//调用 Thread.start()方式启动线程
thread.start();
}
Здесь для сравнения находимrun()
Метод будет немного отличаться из-за разницы в реализацииразница:
ThreadFirstTime передается по наследствуThread
поэтому его можно использовать напрямуюthis
ключевые слова, вы можете видеть, что мы используемthis.getName()
чтобы получить имя текущего потока.
И RunnableFirstTime на нашей стороне реализованRunnable
интерфейс для реализацииrun()
метод, поэтому используйте его здесьThread.currentThread()
чтобы получить текущий объект потока.
Одна вещь, чтобы упомянуть здесьThread.currentThread()
Он очень часто используется и очень полезен, каждый должен использовать его много!
Это же место также очевидноМы видим, что в основном методе мы все通过创建Thread对象来创建线程,同时使用Thread.start()方法来启动线程
, подтверждает ли это утверждение, сделанное нами вначале?创建线程只有一种方式那就是构造Thread类
, не волнуйтесь, давайте посмотримFuturetask
Это то же самое.
(3) БудущееЗадание
FutureTask
путем реализацииCallable
интерфейс и переопределитьcall()
метод для реализации реализации, которая имеет возвращаемое значение. Перейдите непосредственно к коду, а затем объясните его в три шага.FutureTask
Как реализован исполнительный блок потока.
public static class CallerTask implements Callable<String> {
@Override
public String call() throws Exception {
return Thread.currentThread().getName() + "create FutureTask success!";
}
}
public static void main(String[] args) {
//创建异步任务
FutureTask<String> futureTask = new FutureTask<>(new CallerTask());
//创建线程,调用Thread.start()启动线程
new Thread(futureTask).start();
//获取线程返回结果
try {
String result = futureTask.get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
первый шаг:
public static class CallerTask implements Callable<String> {
@Override
public String call() throws Exception {
return Thread.currentThread().getName() + "create FutureTask success!";
}
}
Создайте класс задач, реализуяCallable<String>
интерфейс, переопределениеcall()
метод для реализации класса задач, который можно сравнить здесьRunnable
интерфейс. Но разница между двумяRunnable
Интерфейс реализуетrun()
метод является пустым методом, ноCallable<String>
достигнутоcall()
Вы можете видеть, что возвращаемый параметр в данном примере — это String, который является методом с возвращаемым значением.
второй шаг:
//创建异步任务
FutureTask<String> futureTask = new FutureTask<>(new CallerTask());
использоватьFutureTask
Метод параметризованного конструктора для создания асинхронных задач, которыми может управлять Thread.
третий шаг:
Как и в предыдущих двух методах,new Thread()
Чтобы вручную создать поток, вызовите Thread.start(), чтобы запустить поток.
//创建线程,调用Thread.start()启动线程
new Thread(futureTask).start();
//获取线程返回结果
try {
String result = futureTask.get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
На данный момент мы можем ясно видеть, что черезFutureTask
Реализация потоков, чьи потоки также создаютсяnew Thread()
быть реализованным. Поэтому я считаю, что каждый здесь должен понять вывод, упомянутый в начале, то есть创建线程只有一种方式那就是构造Thread类,其实现方式有三种
.
Интервьюеры, тоже обратите внимание, больше не спрашивайтеКакие есть способы создать нитьТакая проблема, будьте осторожны, чтобы не разорвать руки на месте ~
3. Как работает new Thread() тремя способами?
Посмотрим, насколько свят этот метод
/**
* Allocates a new {@code Thread} object. This constructor has the same
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
* {@code (null, target, gname)}, where {@code gname} is a newly generated
* name. Automatically generated names are of the form
* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
*
* @param target
* the object whose {@code run} method is invoked when this thread
* is started. If {@code null}, this classes {@code run} method does
* nothing.
*/
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
Сначала нажмите, чтобы увидеть этот метод, я думаю, что все не могут в это поверить, потому что участиеRunnable target
? ? ?
В этот момент вы можете поместить курсор на объявление текущего класса, то есть на строку 140.
public class Thread implements Runnable
Меня вдруг осенило, что наш класс Thread тоже реализует интерфейс Runanble. Запустить естественно не проблема, в конце концов, люди - настоящие мастера.
Точно так же мы можем предположитьFutureTask
Он, должно быть, достигRunnable
интерфейс
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V>
Проследив базовое отношение к классу, мы можем увидетьFutureTask
достигнутоRunnableFuture
,RunnableFuture
унаследованоRunnable
иFuture
.
Некоторые друзья могут спросить, java — это не одиночное наследование, это множественная реализация? Почему множественное наследование здесь? Интерфейсы могут быть мультинаследованы, это требует внимания.
Еще один хитрый трюк здесь — выбрать наследование вместо реализации.Runnable
, цель которого состоит в том, что
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
public abstract void run();
Этот абстрактный метод, еслиRunnableFuture
Если вы используете орудия для реализации, следите за нашимиFutureTask
такжедолженвыполнитьrun()
способ, аналогичный нашемуcall()
будут конфликты, поэтому вRunnableFuture
выберите перезаписатьrun()
Его не нужно реализовывать в последующих классах реализации.run()
метод.
Здесь это немного запутанно, но я надеюсь, что вы прочитали код еще дважды, это подробный момент.
Прочитав это соотношение, мы найдемFutureTask
Этот объект может бытьCallable
создавать или черезRunnable
Чтобы достичь творения, принято говорить, чтоFutureTask
то есть поддержкаrun()
также поддерживаетcall()
.
FutureTask<String> futureTask = new FutureTask<>(new CallerTask());
FutureTask<String> futureTask = new FutureTask(new RunnableFirstTime(),"create FutureTask success!");
Учащиеся, интересующиеся отношениями между ними, могут продолжать следовать этому коду и соответствующему описанию:
/**
* Creates a {@code FutureTask} that will, upon running, execute the
* given {@code Runnable}, and arrange that {@code get} will return the
* given result on successful completion.
*
* @param runnable the runnable task
* @param result the result to return on successful completion. If
* you don't need a particular result, consider using
* constructions of the form:
* {@code Future<?> f = new FutureTask<Void>(runnable, null)}
* @throws NullPointerException if the runnable is null
*/
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
Его можно найтиFutureTask
Можно добиться того же эффекта, что и Runnable, но мы вообще так не играем, вообще говоряFutureTask
изcal()
В роли этого класса выступает метод, при отсутствии возвращаемого параметра рекомендуется вызывать его напрямую.Runnable
интерфейс для реализации.
Не забудьте поставить лайк 👍 и подписаться, чтобы продолжать изучать следующий контент вместе со мной~