Синхронизируем ли мы переменные экземпляра, которые являются окончательными? Если да, то какая польза? - PullRequest
3 голосов
/ 03 февраля 2010

Я хотел бы знать, синхронизируем ли мы переменную экземпляра, которая является окончательной.Поскольку переменные являются окончательными, значение не может быть изменено.Кто-нибудь может объяснить это?

Ответы [ 6 ]

11 голосов
/ 03 февраля 2010

Синхронизируем ли мы переменные экземпляра, которые являются окончательными?

Да.Вы все еще должны синхронизировать изменяемые объекты

final! = Immutable

 public class Something {
     private final List list = new ArrayList();

     public void add( String value ) {
         this.list.add( value );
     }
     public int count() {
          return this.list.size();
     }
 }

Позже

 Something s = new Something();

 Thread a = new Thread() {
    public void run(){
       s.add( "s" );
       s.add( "s" );
       s.add( "s" );
     }
 };
 Thread b = new Thread(){
     public void run() {
         s.count();
         s.count();
     }
 };

 a.start();
 b.start();

Если да, то в чем смысл?1017 *

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

Это повышает безопасность (ваш объектный атрибут не может быть заменен вредоносным подклассом). Позволяет оптимизировать компилятор (потому что он знает, что когда-либо будет использоваться только ссылка).

Атрибут, который является как конечным, так и неизменным (например, String, Integer и другие), не нуждается в синхронизации.

class X {
    private final String name = "Oscar";

    public int nameCount(){
        return this.name.length(); //<-- Will return 5 ALWAYS ( actually the compiler might inline it 
    }
 }
5 голосов
/ 03 февраля 2010

Важное исключение: конечные поля, которые не могут быть изменены после создания объекта, могут безопасно считываться с помощью несинхронизированных методов, как только объект сконструирован1005 * Синхронизированные методы

1 голос
/ 03 февраля 2010

Если переменная экземпляра не является неизменной, то состояние этой переменной все еще можно изменить, даже если она является конечной. Взять, к примеру:

private final List<Foo> foos = new ArrayList<Foo>();


public void addFoo(Foo newFoo){
      foos.add(newFoo);
}

public Foo popFoo(){
     return foos.remove(0);
}

В этой ситуации, даже если foos является окончательным, поток B может добавить Foo, пока поток A пытается удалить элемент, что приводит к потенциально значимому состоянию.

Учебное пособие по синхронизированным методам, упомянутое в других примерах, является правильным, так как чтение конечная переменная не нуждается в синхронизации. Однако, если переменная является изменяемой (как List<T>), то запись в эту переменную должна быть синхронизирована, чтобы гарантировать отношение «происходит до». Однако если переменная является неизменной, то, конечно, запись в эту переменную в любом случае запрещена, поэтому синхронизировать не нужно.

1 голос
/ 03 февраля 2010

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

Например:

public class Thing {
   private final List l1 = 
   private final List l2 = 
   private final List l3 = 

   ...

   public void l1ToL2() {
       l2.add(l1.removeFirst());
   }

   public void l2ToL3() {
       l3.add(l2.removeFirst());
   }
}

Если мы не сделаем что-то для синхронизации использования этих методов l1, l2 и l3, они не являются поточно-ориентированными, и одновременные операции из разных потоков могут повредить списки.

С другой стороны, это потокобезопасно по причинам, указанным в ответе @Anthony Forloney.

public class Thing {
   private final int i = ... ;

   public int getI() {
       return i;
   }
}
1 голос
/ 03 февраля 2010

Обычно вы бы не помечали методы доступа как synchronized, если метод возвращал значение конечной переменной;например,

private final String message = "Hello, World";

// No need to mark as synchronized as message can never change.
public String getMessage() {
  return message;
}

Однако вы можете синхронизировать с конечной переменной, например, если вы использовали ее в качестве блокировки;например,

private final Object lock;

...

synchronized(lock) {
  // Do something in critical section.
}
0 голосов
/ 03 февраля 2010

Нет необходимости синхронизировать доступ к конечным переменным экземпляра.

см. http://java.sun.com/docs/books/tutorial/essential/concurrency/syncmeth.html

Синхронизированные методы обеспечивают простую стратегию предотвращения помех потоков и ошибок согласованности памяти: если объект виден более чем одному потоку, все операции чтения или записи в переменные этого объекта выполняются с помощью синхронизированных методов. (Важное исключение: конечные поля, которые нельзя изменить после создания объекта, могут безопасно считываться с помощью несинхронизированных методов после создания объекта). Эта стратегия эффективна, но может создавать проблемы с живостью, так как мы будем см. далее в этом уроке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...