Ваш вопрос говорит о том, что вы, возможно, не полностью понимаете, что означает "нить".
Когда мы учились программировать, они учили нас, что компьютерная программа - это последовательность инструкций, и они учили нас, что компьютер выполняет эти инструкции один за другим, начиная с некоторого четко определенного точка входа (например, процедура main()
).
Хорошо, но когда мы говорим о многопоточных программах, уже недостаточно говорить, что «компьютер» выполняет наш код. Теперь мы говорим, что потоков выполняет наш код. Каждый поток имеет свое собственное представление о том, где он находится в вашей программе, и если два или более потоков выполняются в одной и той же функции в одно и то же время, то каждый из них имеет свою собственную частную копию аргументов функции и локальных переменных.
Итак, Вы спросили:
Является ли Java-программа по умолчанию причиной создания только 1 потока?
Java-программа всегда начинается с одного потока, выполняющего ваш код, и обычно нескольких других потоков, выполняющих код JVM. Обычно вам не нужно знать о потоках JVM. Один поток, который выполняет ваш код, начинает свою работу в начале вашей main()
процедуры.
Программисты часто называют этот начальный поток "основным потоком". Вероятно, они называют это так, потому что это вызывает main()
, но будьте осторожны! Название может вводить в заблуждение: JVM не обрабатывает «основной поток» иначе, чем любой другой поток в многопоточной Java-программе.
если мы создаем многопоточную программу, когда несколько потоков обращаются к одному и тому же коду объекта Java?
Потоки делают только то, что им говорит ваша программа. Если вы напишите код для двух разных потоков для вызова одной и той же функции, то это то, что они будут делать. Но давайте разберем этот вопрос немного ...
... Прежде всего, как создать многопоточную программу?
Программа становится многопоточной, когда ваш код говорит, что она становится многопоточной. В одном простом случае это выглядит так:
class MyRunnable implements Runnable {
public void run() {
DoSomeUsefulThing();
DoSomeOtherThing();
}
}
MyRunnable r = new MyRunnable();
Thread t = new Thread(r);
t.start();
...
Java создает новый поток, когда какой-то другой поток в вашей программе вызывает t.start()
. (ПРИМЕЧАНИЕ! Экземпляр Thread
, t
, не является потоком . Это всего лишь дескриптор , который ваша программа может использовать для запуска потока и запроса о состоянии его потока и управляй им.)
Когда новый поток начинает выполнять программные инструкции, он начинает с вызова r.run()
. Как видите, тело r.run()
приведет к тому, что новый поток будет DoSomeUsefulThing()
, а затем DoSomeOtherThing()
, прежде чем r.run()
вернется.
Когда возвращается r.run()
, поток завершается (a.k.a., "terminated", a.k.a., "dead").
Итак,
когда несколько потоков обращаются к одному и тому же коду объекта Java?
Когда ваш код заставляет их это делать. Давайте добавим строку к примеру выше:
...
Thread t = new Thread(r);
t.start();
DoSomeUsefulThing();
...
Обратите внимание, что основной поток не остановился после запуска нового потока. Он продолжает выполнять все, что пришло после вызова t.start()
. В этом случае следующая вещь, которую он делает, - это DoSomeUsefulThing()
. Но это то же самое, что программа сказала новому потоку! Если для DoSomeUsefulThing()
требуется какое-то значительное время, то оба потока будут делать это одновременно ... потому что это то, что программа сказала им сделать.
, пожалуйста, покажите пример программы, где важна безопасность потоков
Я только что сделал.
Подумайте о том, что может делать DoSomeUsefulThing()
. Если он делает что-то полезное, то он почти наверняка что-то делает с некоторыми данными где-нибудь. Но я не сказал , с какими данными работать, так что есть вероятность, что оба потока одновременно делают что-то с одними и теми же данными.
У этого есть большой потенциал, чтобы не получиться хорошо.
Один из способов исправить это - сообщить функции, над какими данными работать.
class MyDataClass { ... }
Class MyRunnable implements Runnable {
private MyDataClass data;
public MyRunnable(MyDataClass data) {
this.data = data;
}
public void run() {
DoSomeUsefulThingWITH(data);
DoSomeOtherThingWITH(data);
}
}
MyDataClass dat_a = new MyDataClass(...);
MyDataClass dat_b = new MyDataClass(...);
MyRunnable r = new MyRunnable(dat_a);
Thread t = new Thread(r);
t.start();
DoSomeUsefulThingWITH(dat_b);
Там!Теперь два потока делают одно и то же, но делают это с разными данными.
Но что, если вы захотите , чтобы они работали с одними и теми же данными?
Это тема для другого вопроса.Google для "взаимного исключения", чтобы начать.