Эти классы потокобезопасны? - PullRequest
1 голос
/ 23 ноября 2011

Первый класс:

class Main1 {
    private ExecutorService service = Executors.newFixedThreadPool(4);

    public static void main(String[] args) {
       Main1 m = new Main1();
       m.start();
    }

    public void start() {
        final MyObject obj = new MyObject(); 
        obj.doSomeCalculation();//  after this point not to modify obj in main thread

        service.submit(new Runnable(){
            public void run() {
                    obj.doSomething(); // is it threadsafe doing this?
               }
        });
    }
}

Второй класс:

class Main2 {
    private ExecutorService service = Executors.newFixedThreadPool(4);

    public static void main(String[] args) {
        Main2 m = new Main2();
        m.start();
    }

    public void start() {
        class Job implements Runnable {
           public MyObject obj;

            public void run() {
                obj.doSomething(); // is it threadsafe doing this?
            }
        }

        Job job = new Job();
        job.obj.doSomeCalculation(); // after this point not to modify obj in main thread
        service.submit(job);
    }
}

Безопасны ли Main1 и Main2? Имеют ли смысл Main1 и Main2 для безопасности потоков?

обновление: Ни doSomeCalulation (), ни doSomething () не имеют блокировок или синхронизированных блоков. Я хочу знать, может ли doSomething () всегда читать правильные состояния, которые doSomeCalculation () изменяют на obj

Ответы [ 2 ]

3 голосов
/ 23 ноября 2011

Являются ли Main1, Main2 потокобезопасными?

В случае Main1 потокобезопасность приложения зависит от того, является ли MyObject поточно-ориентированным и какие-либо другие потоки работают с ним. Тем не менее, оператор obj.doSomething(); является поточно-ориентированным, при условии, что ничто другое не меняет объект

Фактически, оператор obj.doSomething(); не использует переменную в классе включения. Вместо этого значение этой переменной передается внутреннему классу в скрытом аргументе конструктора. Другая вещь, которая делает этот потокобезопасным, состоит в том, что существует неявная синхронизация между родительским и дочерним потоками, когда создается новый поток. (Ссылка - JLS 17.4.4 Порядок синхронизации ) Эти два факта в совокупности означают, что метод Runnable.run() получит правильную ссылку и что дочерний поток увидит состояние объекта в точке синхронизации ( или позже).

В случае Main2 применяется то же самое. В этом случае вы просто делаете явно (более или менее) то, что происходит неявно в случае Main1.

ОБНОВЛЕНИЕ - приведенные выше рассуждения применимы, даже если вы изменяете объект в родительском потоке (согласно вашему обновленному вопросу) перед передачей его в дочерний поток ... из-за неявной синхронизации, о которой я упоминал , (Однако, если родительский поток должен был изменить MyObject после вызова submit(), вы столкнулись бы с проблемами безопасности потока.)

Имеют ли смысл Main1 и Main2 по-разному?

Я не знаю, что вы спрашиваете. Если вы спрашиваете, есть ли какая-либо польза от использования внутреннего класса, а не анонимного внутреннего класса ... в этом случае ответ отрицательный. Они ведут себя одинаково в отношении безопасности потоков.

На самом деле, версия Main1 лучше, потому что она проще, более читабельна (для опытного разработчика Java) и более устойчива. Класс Main2 предоставляет поле obj для другого кода, который потенциально доступен или даже обновлен. Это плохой стиль. Вы можете исправить это, но только добавив больше кода ... что возвращает нас к простоте / читабельности.

1 голос
/ 23 ноября 2011

Как структурирован ваш код, вы отправляете свою работу (в обоих случаях) только после того, как уже выполнили вышеуказанные вычисления. Таким образом, нет никакой вероятности того, что эти два действия будут происходить параллельно, и поэтому нет гонок данных.

Однако, если вы выполняете свои вычисления после отправки задания / Runnable в службу исполнителя, тогда эти два вычисления могут выполняться параллельно и может происходить гонка данных.

Job job = new Job();
service.submit(job);
// Now there is a data race!!!
job.obj = ...// do some calculation, and after this point not to modify obj in main thread
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...