2016-09-04 5 views
1

Примечание от The Java Параллелизма В практикенеизменных объектов и инициализация безопасность с ниточным

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

Получаю, что они нить - сейф.

Отрывок из Джереми Мэнсона блог-

class String { 
    // Don't do this, either. 
    static String lastConstructed; 
    private final byte[] bytes; 
    public String(byte[] value) { 
    bytes = new byte[value.length]; 
    System.arraycopy(value, 0, bytes, 0, value.length); 
    lastConstructed = this;   
    } 
} 

Поскольку эта ссылка хранится в lastConstructed ", следовательно, Спасаясь конструктор

Ofcourse, он будет работать, если вы сделали lastConstructed летучий (postJDK5 + семантика)

Один из вопросов, заданных здесь, -

Если lastConstructed был изменчивым, но тогда ссылка была небезопасной , опубликованной в другой поток, то строка не будет неизменной. Справа?

, к которому ответ Джереми was-

Это не будет поточно-потому, что он был неизменен, но было бы потокобезопасной потому lastConstructed была нестабильной.

Я прекрасно понимаю, что он был бы потокобезопасным, потому что lastConstructed был изменчивым, но я не получаю Он не был бы потокобезопасным, потому что он был неизменным.

Почему? В записке от Concurrency In Practice говорится, что неизменяемые объекты могут безопасно использоваться любым потоком (например, гарантия безопасности потока). Если что-то неизменное, то это поточно-безопасное.

Просьба предложить.

+0

Вы не можете опубликовать ссылку на объект и ожидать, что он будет использоваться или иметь какое-либо отношение к полю 'static'. –

+0

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

ответ

1

Общее неправильное понимание заключается в том, что у вас есть поля Object в Java. У вас есть только ссылки и примитивы. Это означает, что

static String lastConstructed; 

Поле lastConstructed является изменяемыми ссылки. Видимость не является потокобезопасной. Наличие неизменяемого объекта не связывает никаких свойств с ссылкой на этот объект.

Аналогичным образом, если у вас есть поле final, это не делает ваш объект неизменным.

final Date today = new Date(); 

today не является неизменным только потому, что вы сделали одну ссылку на него final.

Более тонкий в использовании volatile Вы должны быть осторожны в отношении того, читаете ли вы или записываете изменчивое значение. Даже если вы делаете

volatile Date now = new Date(); 

now.setTime(System.currentTimeMillis()); // no thread safe. 

Существует две причины, по которым это не является потокобезопасным. 1) Доступ к now - это не запись, а во-вторых, это происходит до записи в любом случае. После этого требуется барьер для записи. Что-то вы видите, что кажется глупостью.

now = now; // this adds a write barrier. 

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

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

Примечание: добавление некоторых потокобезопасных конструкций может скрыть проблемы безопасности потоков, но это не исправляет их, это просто означает, что некоторые изменения в JVM, или ОС или аппаратном обеспечении нарушат ваш код в будущем.

+1

Заключительные поля не могут быть изменены, хотя объекты они могут быть изменены, если они изменяемы (сегодняшнее поле окончательно и неизменено, но новая Date() изменена). Правильно ли я? –

+0

@ShirgillFarhanAnsari правильно. Объект 'Date' все еще может быть мутирован. –

+0

Если я объявляю lastConstructed как окончательное статическое поле, в этом случае он будет потокобезопасным. Правильно. –

2

Несмотря на то, что @Peter Lawrey объяснил детали и проблемы разработки потоков безопасных и неизменных классов, и, основываясь на дальнейшем обсуждении, я думаю, что вопрос не получил прямого ответа. Итак, я хотел бы немного уточнить:

Основная проблема при понимании фразы «Неизменяемые объекты могут использоваться безопасно с помощью любой темы» заключается в том, что она сама по себе не является полной. Он полагается на предпосылку, что любой объект также должен быть безопасно опубликован, чтобы быть потокобезопасным. Неизменяемые объекты не являются исключением. Итак, полная фраза должна быть «Неизменяемые и безопасно опубликованные объекты могут быть безопасно использованы любой нитью».

Проблема в примере String заключается в том, что она позволяет ссылаться на объект, чтобы убежать от конструктора, тем самым представляя другие потенциально недействительные состояния объектов. Например, если компилятор решает оптимизировать конструктор и переставить операции по причинам работы так:

public String(byte[] value) { 
    bytes = new byte[value.length]; 
    lastConstructed = this;   
    System.arraycopy(value, 0, bytes, 0, value.length); 
} 

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

Здесь мы приходим к значению фразы «Это не было бы потокобезопасным, потому что оно было неизменным». Это означает, что неизменность сама по себе не гарантирует безопасность потока, и пример доказывает ее.

Выполнение lastConstructed volatile заставит компилятор испускать барьер памяти, который помешал бы оптимизатору перегруппировать операции описанным выше способом. Это гарантировало бы, что копирование массива всегда происходит до назначения lastConstructed = this. В результате другой поток, который читает lastConstructed, никогда не увидит недостроенную строку. Это также гарантирует, что другие потоки всегда будут считывать фактическое значение lastConstructed. Вот почему «это было бы поточно-безопасным, потому что lastConstructed был volatile» в этом конкретном случае.

+0

Предположим, что String.lastConstructed объявлен окончательным. Теперь представьте, что один поток выполняет (чтение) String.lastConstructed, в то время как параллельно, другой поток создает объект вышеуказанного класса String. Какова будет ценность String.lastConstructed, как видно из первого потока. –

+0

@ShirgillFarhanAnsari, 'lastConstructed' не может быть окончательным, он не будет компилироваться, потому что он статичен, и вы переназначаете его в конструкторе экземпляра. 'final' означает, что значение поля не может быть изменено после первого присвоения. –

+0

Я правильно говорю, что статическая инициализация происходит до окончательной инициализации. –

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