Используя __init__.py
файл позволяет Вам делать внутреннюю структуру пакета невидимой из внешней стороны. Если внутренняя структура изменяется (например, потому что Вы разделяете один толстый модуль на два), только необходимо корректироваться __init__.py
файл, но не код, который зависит от пакета. Можно также сделать части пакета невидимыми, например, если они не готовы к общему использованию.
Примечание, которое можно использовать эти del
команда, таким образом, типичное __init__.py
может быть похожим на это:
from somemodule import some_function1, some_function2, SomeObject
del somemodule
Теперь, если Вы решаете разделить somemodule
, новое __init__.py
могло бы быть:
from somemodule1 import some_function1, some_function2
from somemodule2 import SomeObject
del somemodule1
del somemodule2
С внешней стороны пакет все еще смотрит точно как прежде.
Да, это безопасно, даже если исполнитель заменил свой поток посередине. Начало / завершение потока также являются точками синхронизации.
http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.4.4
Простой пример:
static int state;
static public void main(String... args) {
state = 0; // (1)
Thread t = new Thread() {
public void run() {
state = state + 1; // (2)
}
};
t.start();
t.join();
System.out.println(state); // (3)
}
Это Гарантируется, что (1), (2), (3) хорошо упорядочены и ведут себя так, как ожидалось.
Для однопоточного исполнителя «Задачи гарантированно выполняются последовательно», он должен каким-то образом определять завершение одной задачи перед запуск следующего, который обязательно правильно синхронизирует разные run ()
Пока это всего лишь один поток, нет необходимости делать его изменчивым. Если вы собираетесь использовать несколько потоков, вам следует не только использовать volatile, но и синхронизировать. Увеличение числа - это не атомарная операция - это распространенное заблуждение.
public void run() {
synchronize (this) {
if (this.state != -1)
this.state++;
}
}
Вместо использования синхронизации вы также можете использовать AtomicInteger # getAndIncrement () (если раньше вам не понадобился if).
private AtomicInteger state = new AtomicInteger();
public void run() {
state.getAndIncrement()
}
Ваш код, в частности этот бит
if (this.state != -1)
this.state++;
, потребует атомарной проверки значения состояния, а затем приращения к состоянию в параллельном контексте. Таким образом, даже если ваша переменная была изменчивой и задействовано более одного потока, у вас были бы проблемы с параллелизмом.
Но ваш дизайн основан на утверждении, что всегда будет только один экземпляр Test, и , этот единственный экземпляр предоставляется только одному (тому же) потоку. (Но обратите внимание, что единственный экземпляр на самом деле является общим состоянием между основным потоком и потоком-исполнителем.)
Я думаю, вам нужно сделать эти предположения более явными (в коде, например, используйте ThreadLocal и ThreadLocal. получить()). Это сделано для защиты как от будущих ошибок (когда другой разработчик может по неосторожности нарушить предположения проекта), так и,
Если ваш ExecutorService является однопоточным, то общего состояния нет, поэтому я не понимаю, как здесь могут быть какие-то проблемы.
Однако не было бы больше смысла в передавать новый экземпляр вашего класса Test
каждому вызову execute ()
? т.е.
for (int i=0; i<10; ++i)
ex.execute(new Test());
Таким образом, общего состояния не будет.
Изначально я думал так:
Если бы задача всегда выполнялась той же нити, не было бы проблема. Но
Excecutor
произведеноnewSingleThreadExecutor ()
может создавать новые потоки, чтобы заменить те, которые убиты по любой причине. Здесь нет гарантия о том, когда замена поток будет создан или какой поток создаст его.Если поток выполняет некоторые записи, то вызывает
start ()
в новом потоке, те записи будут видны новым нить. Но нет никакой гарантии, что это правило применимо и в данном случае.
Но неоспоримое право: создание правильного ExecutorService
без достаточных барьеров для обеспечения видимости практически невозможно. Я забыл, что обнаружение смерти другого потока - это связь синхронизируется с . Механизм блокировки, используемый для простоя рабочих потоков, также потребует барьера.
Это нормально, чтобы состояние было энергонезависимым в этом конкретном коде, потому что существует только один поток, и только этот поток обращается к полю. Отключение кэширования значения этого поля в единственном потоке, который у вас есть, просто снизит производительность.
Однако, если вы хотите использовать значение состояния в основном потоке, который запускает цикл, вы должны сделать поле изменчивым :
for (int i=0; i<10; ++i) {
ex.execute(test);
System.out.println(test.getState());
}
Однако даже это может работать некорректно с volatile, потому что нет синхронизации между потоками.
Поскольку поле является частным, проблема возникает только в том случае, если основной поток выполняет метод, который может получить доступ к этому полю .