Pull to refresh

Comments 97

Запустить новый поток можно двумя способами:

Еще можно через новый интерфейс java.util.concurrent.Callable

И еще один момент — InterruptedException нежелательно поглощать без обработки (пустой catch блок), его нужно либо повторно генерировать, либо заново выставить флаг методом interrupt(), чтобы еще «прервать» текущий поток. Иначе другие методы в стеке вызовов не узнают, что было прерывание.
Да, про Callable я забыл, а если честно то и вовсе не знал) Насколько я понимаю возвращение результата работы потока используется не слишком часто, но, все равно, напишу об этом в следующей статье.

Насчет исключения — честно говоря я не очень понимаю зачем другим методам в стеке знать о возникнувшем исключении, если после его возникновения требуется просто продолжить выполнение вызвавшего метода. Может поясните?
Насколько я понимаю возвращение результата работы потока используется не слишком часто

Чаще используется возможность выбросить исключение.

честно говоря я не очень понимаю зачем другим методам в стеке знать о возникнувшем исключении,

Допустим вы в рамках одного потока поймали InterruptedException, например, в методе sleep и поглотили его без выставления флага. Если дальше по коду (или выше по стеку) стоят проверки флага Interrupted — то они не сработают
Допустим вы в рамках одного потока поймали InterruptedException, например, в методе sleep и поглотили его без выставления флага. Если дальше по коду (или выше по стеку) стоят проверки флага Interrupted — то они не сработают


Ну, это было бы косяком если бы кто-то собирался прерывать потоки из примеров) В примере с объяснением прерываний обработка исключения выполняется (а именно вызывается return), в других примерах особого смысла раздувать код не вижу. Однако в более «жизненных» ситуациях работать с исключениями, конечно, надо по-другому.
Вы, наверное, имеете большой опыт проведения собеседований по Java Concurrency…
Статья освежила память, спасибо.
Но вот за
class SomeThing			//Нечто, реализующее интерфейс Runnable
implements Runnable		//(содержащее метод run())

мне бы за такое руки оторвали в универе.
*спрятал руки за спину* хыхы)
Освежила память? Мне казалось это основы, которые должны после недельного запоя от зубов отскакивать :)
Порой после длительного перерыва, даже основы забываешь, к сожалению.
Простите, а что здесь не так? (без сарказма)
а что не так с этим кодом?
Наверное речь о комментариях.
Разделение на главный/побочный потоки возникает лишь в голове программиста, на уровне JVM таких разделений нет.
Согласен, в Java потоки равноправны.
Не совсем, точнее даже так, не для каждой JVM. У треда как минимум может быть приоритет, который поддерживается не всеми ОС. Но так же тред может быть помечен как daemon, т.е. он не будет для JVM считаться полноценным тредом и и она, например, может его попросту убить, если все нормальные (т.е. не daemon) треды будут убиты или закончат свое исполнение
private boolean mFinish = false;
Обязательно volatile, иначе другой поток может не увидеть изменений данной переменной, сделанных в методе finish() другим потоком (при опции jit компилятора -server).
Хм, спасибо. Думаю в данном примере это не критично, но о volatile расскажу в следующей статье.
Ничего себе «не критично». Вы не объявили переменную volatile, не используете synchronized блок, пишите в неё из одного потока, а читаете с другого — по спецификации Вам не гарантируется, что изменения в переменной другой поток увидит даже к завтра. И Вы хотите учить других такой статьей?
Я понимаю ваше негодование, но формат статьи не позволяет описать все и сразу. В нормальном режиме компиляции программа работает. На данном этапе не вижу смысла требовать большего. Никто не ринется писать многопоточные программы, сразу после прочтения данной статьи, пока не разберется что к чему. Статья служит точкой старта а не полным руководством. О volatile, syncronized и разделении ресурсов между потоками будет рассказано в следующей статье.
А причём тут другая статья? Код в этой статье написано неправильно, он вполне может повиснуть на одной из реализаций виртуальной машины, и это будет валидное поведение. Я бы сказал, что это даже более вероятно, и если оно где-то работает, то это больше чудо в следствии недоработанного оптимизатора.
Вам лучше исправить это, а не рассказывать, что это «не критично» да про какой-то «нормальный режим компиляции».
Хорошо, согласен, исправил
зато формат статьи позволяет написать правильный код c volatile, сказав, что про это слово будет следующая статья.
А смысл, зачем еще один пересказ? Об этом писали уже сто раз, в том числе и на хабре, можно просто ссылку оставить.
по ссылке хорошая статья, но более серьёзная. Тут статья детская совсем. Так что это не повтор.
Спасибо большое автору за пост, посоветуйте пожалуйста еще статьи и книги о потоках, желательно из реальной жизни, а то большинство материала довольно абстрактно или устарело. Было бы замечательно увидить еще посты на данную тематику.
По Java есть отличная книжка «Эккель Б. — Философия Java». Пережила четыре издания и имеет хорошие отзывы. Почти все мои знания по Java — из неё. О потоках же я в основном читал из документации Oracle. Конкретно о потоках — здесь.
Для начала сойдет, но как сказано документация и исходники — вот основа знаний о Java.
Касательно многопоточности могу посоветовать след. книгу — «Concurrency in practice»(если не ошибаюсь только на англ. языке).
А еще Java 2: наиболее полное руководство. Шилдт
Шилдт пишет обо всем подряд, я не очень доверяю таким авторам. Хотя опять же для начинающего сойдет.
Шилдт человек с мировым именем, Ему тяжело не доверять. Да и манера изложения у него приятная. Так что подойдет не только новичкам. А для профи все же рекомендую читать доки oracle.
Прошу прощения за немного сумбурный комментарий.
+1 к Шилдту. Буквально час назад читал в нём про Fork/Join Framework.
читайте Java Concurrency in Practice. Там всё есть. И без грубых ошибок, как в этой статьи.
Спасибо за советы и названия книг, Эккеля читал, но слишком много описания классов и мало понятно, как все это в итоге использовать. Про Шилдта забыл, честно говоря, хотя самая пожалуй очевидная книга, которую надо было смотреть. Java Concurrency in Practice — много о ней слышал, надо будет купить. Мне больше всего понравился подход в книге — Java 7 Recipes A Problem-Solution Approach, там то, как раз показано на примерах, как применять многопоточность и как распараллеливать алгоритмы, но примеров к сожалению не так много.

Основная моя проблема и моих коллег по работе, в том, что имея более пяти лет опыта разработки на java никто из нас ни разу не использовал многопоточность. Для меня до сих пор не вполне ясно, какие проблемы и как я могу решить используя multi-threading. Хотелось бы простого объяснения вида: проблема -> решение +объяснение подводных камней, а не только описания всех классов пакета, с тоннами кода.
Основная моя проблема и моих коллег по работе, в том, что имея более пяти лет опыта разработки на java никто из нас ни разу не использовал многопоточность.

так может, оно Вам и не надо? Может, это и не проблема?

Для меня до сих пор не вполне ясно, какие проблемы и как я могу решить используя multi-threading

дык зачем Вам многопоточность тогда?

Хотелось бы простого объяснения вида: проблема -> решение +объяснение подводных камней, а не только описания всех классов пакета, с тоннами кода.

читайте Java Concurrency in Practice
Нет проблемы — это лучшее состояние!

Многопоточные программы писать трудно, а хорошие многопоточные программы — очень трудно. Там проще налажать. Поэтому если у меня есть возможность написать в один поток — я всегда пишу в один. И только если я не вижу других путей для ускорения — начинаю заморачиваться с многопоточностью, стараясь при этом использовать примитивы высокого уровня.

Особая тема — многопоточность и GUI. Но это совсем отдельная песня.
Интересное мнение и точка зрения, обязательно учту. Область деятельности у меня не связана с промышленной разработкой, а связана с академической и научной деятельностью и я, как и окружающии меня люди, не привык думать о том как распараллелить алгоритм и меня этому никто не учил и спросить тоже неукого. Вот, если к примеру, я анализирую объекты при помощи reflection и объектов десятки тысяч, то думаю для такого рода алгоритмов можно использовать многопоточность и это даст определенный прирост в производительности.

Именно об этом и хотелось бы прочесть, как и зачем использовать многопоточность и будет ли игра стоить свеч. Да и скилл программирования тоже хочется улучшить.
Основная моя проблема и моих коллег по работе, в том, что имея более пяти лет опыта разработки на java никто из нас ни разу не использовал многопоточность.
Я думаю следует сказать спасибо всем тем, кто позволил большинству программистов даже не задумываться о том, что они в 99.9% случаев используют многопоточность в решении своих проблем. Остальные 0.089 — это студенты которые решают всевозможные академические задачи, и оставшиеся — люди, которые понимают почему они отказываются от многопоточности.

Хотелось бы простого объяснения вида: проблема -> решение +объяснение подводных камней, а не только описания всех классов пакета, с тоннами кода.
Проблема: игровой сервер, способный тянуть на себе несколько тысяч пользователей на одной ноде. Соединения постоянные, игроки всю игру взаимодействуют друг с другом (пачками по 2-9 человек).
Решение: netty в для обработки клиент-серверного и сервер-серверного (серверов то может быть много) взаимодействия, что бы не писать всю низкоуровневую мишуру с соединениями вручную. Остальное — многопоточность.

Объяснение: все, что есть сейчас в мире java для EE не подходит, потому что потребуется либо 20+ серверов, что экономически не выгодного. Либо попросту не будет справляться с такими нагрузками. Ну и главный аргумент — оно все http, что нам категорически не подходит.
Да что вы, какая же это грубая ошибка, так, мелочь: подумаешь в одном случае другие потоки не смогут увидеть изменения переменной (отсутствие volatile), а в другом так и вовсе в гонке мы можем потерять несколько инкрементов\декрементов (отсутствие синхронизации).

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

И да, вы хотя бы посмотрели исходники прежде чем писать глупости про Thread.sleep() например.
Volatile добавил, про необходимость синхронизации написал в заключении. Что не так с методом Thread.sleep()?
То есть ты хочешь, что бы я сюда скопировал код данного метода?
Хорошо, если вы сами не в состоянии, кину код сюда:

    /**
     * Causes the currently executing thread to sleep (cease execution) 
     * for the specified number of milliseconds plus the specified number 
     * of nanoseconds, subject to the precision and accuracy of system 
     * timers and schedulers. The thread does not lose ownership of any 
     * monitors.
     *
     * @param      millis   the length of time to sleep in milliseconds.
     * @param      nanos    0-999999 additional nanoseconds to sleep.
     * @exception  IllegalArgumentException  if the value of millis is 
     *             negative or the value of nanos is not in the range 
     *             0-999999.
     * @exception  InterruptedException if any thread has interrupted
     *             the current thread.  The <i>interrupted status</i> of the
     *             current thread is cleared when this exception is thrown.
     * @see        Object#notify()
     */
    public static void sleep(long millis, int nanos) 
    throws InterruptedException {
	if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
	}

	if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
				"nanosecond timeout value out of range");
	}

	if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
	    millis++;
	}

	sleep(millis);
    }
Хм, получается время ожидания все-равно округляется до миллисекунд?
Вообще можно было не исправлять. У Вас же в статье нигде не говорится, что речь идёт про конкретную реализацию VM, тем более от Oracle. Oracle может и округлили (для совместимости между различными ОС, т.к. в венде с наносекундами имплементировать паузу сложно), а другие могут и не округлять. Например, Google в своей Dalvik VM использует эти наносекунды без округлений.
Ну теперь там есть фраза «При вызове этого метода JVM честно передает системе наносекунды, если система это поддерживает», а это неверно. Linux всё поддерживает, но Oracle JVM это не юзает. Нельзя же говорить за все реализации JVM.
Я вернул все назад, первый вариант был самым лояльным.

p.s. а линукс разве поддерживает интервалы прерываний меньше 1мс? Пока я искал информацию по методу sleep я натыкался на высказывания по поводу 10мс у windows и 1мс у линукса. Или я в чем-то заблуждаюсь?
Как-то плохо Вы гуглили. Первые ссылки в гугле ведут на пост в блоге разработчика Oracle VM, где есть фраза: «On Solaris and Linux these calls take time structures that allow microsecond, or nanosecond, values to be passed — but there is no guarantee of that resolution being achieved». В Linux точность таймера очень сильно зависит от того, с какими параметрами скомпилировано было ядро.
Отличное чтиво, но, как я помню там совершено не раскрывается тема, а почему же, например нельзя просто использовать boolean флаг и «атомарно» переключать его без атомиков и локов, так, пара предложений что это далеко не атомарно. Отличное чтиво, но начинать с него не самая лучшая затея.
Зря ничего не написано про синхронизацию потоков прекрасными методами wait и notify.
Синхронизация — довольно большая тема, я не хотел растягивать статью. О синхронизации я напишу в следующей статье.
Не знаю, к счастью или сожалению, но после того, что предлагает пакет java.util.concurrent методы wait и notify ставновятся уже не такими прекрасными. Про опыты практического примеренния дуамаю даже не стоит и вспоминать
Ну почему, есть задачи, которые через synchronized/wait/notify решаются несколько лаконичнее, чем через аналогичные ReentrantLock/lock/unlock из java.util.concurrent
Хотелось бы увидеть пример задачи. Особенно с wait/notify. Вот по поводу synchronized — я еще согласен, написал его в объявлении метода и можно спать спокойно.
Пример задачи. Поток каждые 60 секунд сканирует папку на наличие изменений:

public void loop()
{
    for (;;)
    {
        scan();
        synchronized(lock)
        {
            lock.wait(60, TimeUnit.SECONDS);
        }
    }
}


Теперь нужно добавить метод, который принудительно сканирует папку, т.е. пробуждает поток из состояния ожидания и делает немедленный скан.

public void forceScan()
{
    synchronized(lock)
    {
        lock.notify();
    }
}


С ReentrantLock все было бы то же самое, но чуть длиннее
Кстати, насчет wait/notify. Посмотрите класс Monitor из библиотеки Guava. Это более простая и удобная альтернатива стандартным Lock/Condition. Там есть несколько принципиальных отличий: не нужно вызывать методы signal/signalAll, не нужно проверять условие в цикле. В общем, классная вещь, но к сожалению, пока только Beta.
Если я не ошибаюсь, то Monitor это просто сильно упрощенный ReentrantLock, его же и использующий
Ну да, он более упрощенный. Но от этого не менее гибкий. Задачи позволяет решать те же самые.
Про синхронизацию «прекрасными» методами wait, notify/notifyAll, join, yield следует написать следующее: «используйте только в крайнем случае, например, при решении задач на собеседованиях, если вам явно указали что следует использоваться только данные примитивы»
не зря. Ничего замечательного в них нету. С Java5 приехало много высокоуровневых примитивов. Насколько я помню, сейчас рекомендуется использовать именно их, а wait-notify только в двух случаях:
1. иначе никак
2. legacy code

Хотя для первого случая есть мозг, а для второго — рефакторинг.
Есть ещё третий случай, когда использование низкоуровневых средств оправдано требованием быстродействия.
ооо, а можно пример в студию?
Те «высокоуровневые примитивы» построены обычно как обёртки над низкоуровневыми, т.е. быстрее быть не могут по определению. Потому иногда имеет смысл написать свой велосипед, не такой универсальный как стандартный, а заточенный под конкретную задачу. Пример такого велосипеда приводить не вижу смысла.
Те «высокоуровневые примитивы» построены обычно как обёртки над низкоуровневыми,

не всегда. Всё чаще это Unsafe, быстрее которого сделать сложно.

т.е. быстрее быть не могут по определению.

а нативно?

Потому иногда имеет смысл написать свой велосипед, не такой универсальный как стандартный, а заточенный под конкретную задачу.


ну если вы — твиттер, гугл, фейсбук или дойчбанк, то да, безусловно. В остальных случаях — не уверен.
> а нативно?
Нативно реализовать на Си? Ну оно то будет быстрее, но там Вы точно не будете юзать те «высокоуровневые примитивы» о которых говорили. ;)

>ну если вы — твиттер, гугл, фейсбук или дойчбанк
Вот как раз они могут себе позволить купить лишний сервер. И потому иногда не заморачиваются, а отдают приоритет надёжности и проверенности. А нам, простым смертным, приходится извращаться.
с нативностью — это вообще отдельная история, давайте забудем об этом.

Я уточню свою мысль: высокоуровневыми примитивами из java.uril.concurrent можно обойтись в 99 процентах случаев.

Что касается крупных компаний — речь идёт не о лишем сервере, а о лишних десятках, сотнях и даже тысячах серверов. Поэтому им имеет смысл поинженерить. Тот случай, когда пара инженеромесяцев выйдут дешевле закупки сотни серверов. А вот для мелких компаний — дешевле часто купить сервер, чем тратить инженеро-месяцы на решение задач оптимизации.
Свой велосипед просто может глючить по началу, крупным компаниям такое не к лицу, потому в некоторых случаях предпочтительнее использовать проверенные средства.
А вот для мелких точно так же может оказаться дешевле потратить месяц работы программиста с окладом 1000 долларов, чем купить новый сервер ценой в 10000 долларов, особенно если их просто нет. Пускай даже потом придётся 3 месяца ещё вылавливать баги.
Ну… это уже экономика, думаю её тоже можно оставить в покое.
ну и насчёт
«высокоуровневые примитивы» построены обычно как обёртки над низкоуровневыми


Загляните в код AtomicInteger, что ли… Или в Semaphore. Найдите там хоть один wait или notify…
Да, соглашусь, в данном случае это обёртки не над wait/notify.

Но AtomicInteger — это вообще из другой оперы… там и не надо wait/notify. Если уж сравнивать, то с классом, предоставляющим аналогичные возможности, там Condition, например. Вот если дойдут руки попробую даже как-то сравнить количественно разность в скорости работы для типовой задачи.
если дойдут руки попробую даже как-то сравнить количественно разность в скорости работы для типовой задачи.


Задолбаетесь сравнивать. Слишком много граблей, на которые можно наступить. И слишком много конфигураций придётся проверить.

Если уж сравнивать, то с классом, предоставляющим аналогичные возможности, там Condition, например.


у него две реализации: ConditionObject в AbstractQueuedSynchronizer и в AbstractQueuedLongSynchronizer. Через первый работает куча java.util,concurrent-примитивов: Semaphore, CountDownLatch, ReetrantLock, ThreadpoolExecutor и пр. Второй — вариант AbstractQueuedSynchronizer для специального случая.

AtomicInteger — это вообще из другой оперы… там и не надо wait/notify

Речь о том, что есть целый класс высокоуровневых примитивов, которым wait/notify вообще ни в какое место не впились.
Это когда JIT вырезает синхронизацию посредством synchronized?
Вы имеете ввиду случай, когда JIT оптимизирует вход/выход в монитор? Ну это тоже — модный ReentrantLock, который использует CAS-инструкции и должен быть теоретически куда быстрее стандартного явовского монитора (блоки synchronized), оказывается очень часто медленнее. :)
Я имею в виду то, что JIT умеет вырезать полностью всю синхронизацию если она не требуется. По поводу стандартного монитора могу сказать вот что…
Во-первых это три типа синхронизации: biased, thin и fat, а не одна.
biased — самый простой и быстрый тип блокировки, при помощи cas-а (как я помню единственного) биндится к одному и тому же потоку, если он постоянно захватывает монитор. Накладные расходы минимальны. Если в дело вступает другой поток, то тип меняется на следующий
thin — перепривязка монитора происходит уже каждый раз с cas-ом. Происходит до тех пор пока множество потоков не начинает биться за ресурс и дальше мы спускаемся выше, к fat
fat — тут уже используются примитивы ос, что весьма и весьма затратно.

Собственно я думаю я ответил на вопрос почему non-fair* RL часто медленнее стандартного монитора (много оптимизаций со стороны JVM, в то время как RL не трогается)

*fair по своей сути будет в тысячи раз медленнее.
UFO just landed and posted this here
это камент из серии «А я вот ещё про ReentrantLock читал!». Стыдоба.
Я вам чем то не угодил? Вашу позицию я понял — кг/ам. Хотите и знаете чем сделать статью лучше и что в ней не правильно — с удовольствием почитаю и исправлю. Не хотите/не знаете — перестаньте, пожалуйста, писать бессмысленные комментарии.
1. вообще-то, это было не Вам :) К Вам были конкретные претензии, вполне раскрытые юзером ashofthedream. Кажется, Вы многое поправили, и это правильно.

2. я сам разберусь, что мне писать, а что нет.
Это было обращено к тому, кто хотел помочь, вместо того чтобы выделываться. Никаких претензий к комментарию aleks_pingvin'a не имею и в плане на статью добавил пометку на ReentrantLock и барьеры. В чем смысл вашего комментария я понять не могу… Унизить? Выпендриться? Или одно другого не исключает?
если Вы хотите выяснить отношения — пишите в личку. На мой взгляд, здесь не место подобным разговорам.
UFO just landed and posted this here
> В частности, в примерах не используется синхронизация

Это сильно, да :)

Вообще в современном мире Java есть гораздо более адекватные способы работы с потоками, их пулами, асинхронным выполнением и прочим. Не в обиду автору — но если кто серьезно собирается «черпать знания» из статьи где автор не понимает зачем volatile нужен в проверке условия выхода из потока, то я таким настоятельно советую поискать более другие источники знаний.
Сильно то, что не используется синхронизация или сильно то, что в фразе не правильный смысл?)

Volatile — добавил. Зачем он нужен — понял. Есть еще замечания?

Да, и я совсем забыл про то, как называл себя в статье великим знатоком многопоточного программирования, несущим знания в народ… хотя подождите, я же этого не писал. Хм, наверное потому что я так не думаю. Вот ведь интересно, с чего все остальные взяли обратнное? Наверное потому же почему люди верят в то, что по телеку вещают только величайшие умы современности, просто не способные ошибаться из-за своей безграничной крутости и приближенности к богу.
Строго говоря, к автору статьи у меня вопросов нет, и на вопрос " Есть еще замечания?" я не стану повторять многое из того, что критики до меня сообщили. Замечу, что если бы мне на интервью показали такое понимание работы в многопоточной среде, то я бы… не удовлетворился.

Но речь не о том. А речь о том, что я с удивлением обнаружил, что подобного рода статья от ну очень уж «не знатока многопоточного программирования» вызывает живой народный интерес, и прочитав ее он (народ) действительно проникнется странной идеей, что «Наиболее очевидная область применения многопоточности – это программирование интерфейсов». А после этого начнет эту многопоточность в интерфейсы вставлять. Что само по себе очень скользкая область, да и к тому же полностью обойденные вниманием проблемы синхронизации, конфликтов/гонок и вся специфика работы с muttable сущностями в конкурентной среде преподнесут массу неприятных и неожиданных сюрпризов. Ну и не говоря о том, что так, как описано в примерах, на таком уровне ручного управления Threads, в реальной жизни стараются не пользоваться, т.к. есть более адекватные и управляемы средства.

Всем, кому интерсно мое мнение, я советую вдумчиво почитать Java Concurrency in Practice. Там все ясно, доступно и разумно рассказано.
UFO just landed and posted this here
> интересно зачем потоку самому вызывать этот метод?! Очевидно что когда он живой результат всегда false
А вот тут Вы сильно ошибаетесь, interrupted — это просто флаг. Поток может быть живым, а interrupted == true. Обычно этот флаг устанавливают из другого потока в целях уведомить данный поток о чём-то, например, что пора завершаться. А в самом потоке Вы уже проверяете этот флаг в нужном месте (или просто ловите исключение) и корректно завершаете работу. Но теоретически можно использовать этот флаг и в других целях.
… ну ладно по быстрому: notify() — ничего не сказано, для sleep() — это тоже самое что для минуса плюс. Кстати и про yield() забыли.


Насколько я знаю notify() работает в паре с wait(), а не с sleep().
Про yield() написал, спасибо.

Про сокеты убрал, пример, и правда, не удачный.

Объяснение про процессы много места не заняло. Оно дано для общей информации и без особого смысла. Без него параграф про процессы был бы суховатым.

Неточность в фразе про создание процесса исправил, еще раз спасибо.

Префикс «m» в имени переменной значит «member», а именно — член класса. Этот префикс служит для отличения переменных класса от переменных метода, в которых префикс не указывается. Вероятно вы подумали что он значит «my»…

Исправил текст про предугадание выполнения программы.

Насчет interrupted() я не очень понял. Именно этот метод вызавается потоком для проверки того, прерван он или нет. Метод interrupt() не убивает поток, а выставляет флаг прерывания, который потом проверяется методом interrupted().

Предложение в параграфе приоритета потоков убрал.
UFO just landed and posted this here
Для пущего укорочения кода

Можно еще более укоротить код использовав анонимный внутренний класс

public class Program		//Класс с методом main().
{
	public static void main(String[] args)
	{
		//Создание и Запуск потока
		new Thread(new Runnable()
		{
			public void run() //Этот метод будет выполняться в побочном потоке
			{
				System.out.println("Привет из побочного потока!");
			}
		}).start();

		System.out.println("Главный поток завершён...");
	}
}
Спасибо, самое то для начинающих.
PS Ну и где же следующая статья про синхронизацию? :)
Sign up to leave a comment.

Articles

Change theme settings