2010-05-09 3 views
7

Классический пример простого сервера:Каков эффект объявления окончательной переменной в методах?

class ThreadPerTaskSocketServer { 
    public static void main(String[] args) throws IOException { 
     ServerSocket socket = new ServerSocket(80); 
     while (true) { 
      final Socket connection = socket.accept(); 
      Runnable task = new Runnable() { 
       public void run() { 
       handleRequest(connection); 
       } 
      }; 
      new Thread(task).start(); 
     } 
    } 
} 

Почему должен Socket быть объявлен как final? Это потому, что новый Thread, который обрабатывает запрос, может ссылаться на переменную socket в методе и вызывать какой-то ConcurrentModificationException?

ответ

13

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

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

+0

Большое спасибо. – Finbarr

2

Вам необходимо объявить его окончательным, а не только. Без этого компилятор не может использовать его в анонимной реализации класса Runnable.

0

Локальные переменные не разделяются между потоками. (Локальная переменная является частью записи активации, и каждый поток имеет свою собственную запись активации).

С connection - это локальная переменная, ее невозможно разделить между потоками. Поскольку он не разделяется между потоками, вам нужно сделать это final, поэтому не имеет значения, что это локальная переменная (ее можно увидеть скорее как постоянное значение).

0

Он не предназначен для решения проблемы ConcurrentModificationException. Любая локальная переменная, используемая внутри метода-вложенного класса (например, анонимный внутренний класс), должна быть объявлена ​​окончательной. См аналогичное обсуждение с последней недели здесь:

method local innerclasses accessing the local variables of the method

На самом деле в случае потоков существует незначительный вклад здесь безопасность потока; не будет видимых проблем с конечной переменной между потоками. Однако это не гарантирует безопасность потоков.

3

Рассмотрим следующий пример:

class A { 
    B foo() { 
    final C c; 
    return new B() { 
     void goo() { 
     // do something with c 
     } 
    } 
    } 
} 
// somewhere else in the code 
A a = new A(); 
B b = a.foo(); 
b.goo(); 

Если с был не окончательным, когда вы достигнете b.goo(), это будет указывать на мусор, так что с будет сборщиком мусора - локальная переменная после окончания вызов метода.

+0

Вы подразумеваете, что конечные переменные не собираются собирать мусор? Это неверно. – Pindatjuh

+0

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

+0

@Pindatjuh - Я подразумеваю, что конечные переменные не получают мусор-цанги *, когда метод заканчивается *. @Eyal - Вы правы, язык мог быть лучше разработан и т. Д. Однако текущий дизайн - это то, что у нас есть ... –

2

Объявление переменной переменной метода означает, что ее значение не может измениться; что его можно установить только один раз. как это применимо в этом контексте?

Я знаю об этом ограничении с анонимными классами в течение некоторого времени, но я никогда не понимал почему. я вижу, что никто до сих пор не делает ни одного из ответов до сих пор. некоторые googling поднялись ниже, что, я думаю, хорошо объясняет это.

анонимный локальный класс может использовать локальные переменные , поскольку компилятор автоматически дает класс а частное поле экземпляра для хранения копии каждой локальной переменной класса использует. Компилятор также добавляет скрытые параметры каждому конструктору в . Эти автоматически создаются автоматически созданные частные поля. Таким образом, локальный класс фактически не имеет доступа к локальным переменным , а просто к его собственным частным копиям их. Единственный способ, которым это может быть , работает правильно, если локальные переменные объявлены окончательными, так что они не будут изменены. С этой гарантией на месте, местный класс уверен, что его внутренние копии переменных точно отражают фактические местные переменные .

кредит: http://renaud.waldura.com/doc/java/final-keyword.shtml#vars

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

+0

Отличный ответ, бит технический. Мне нравится добавлять **, почему ** это нуждается в гарантии: поскольку внутренний класс может быть GCed, и если локальная переменная изменяется позже, и это изменение должно быть отражено в экземпляре внутреннего класса, который является GCed: проблема избегается, если переменная является окончательной. – Pindatjuh

+0

'Объявление конечной переменной метода означает, что ее значение не может измениться; что его можно установить только один раз. На самом деле, нет. Это означает, что ** ссылка ** на объект не может быть изменена. Значение по-прежнему можно изменить несколько раз. (Если объект не является примитивным) – mercury0114

 Смежные вопросы

  • Нет связанных вопросов^_^