предисловие
Разница Строка, StringBuilder, StringBuffer - это что? Эта оценка каждая во всех вопросах Java Face должна встречаться. Когда смутно помните первое собеседование, когда интервьюер задал мне этот вопрос, думая так, какая разница делает все вещью строки. После глубокого понимания этой проблемы и обнаружил, что не просто?
Закуска
интервьюер: Привет, ты другой технарь, не так ли?
домик: Привет, интервьюер, я другой технарь.
интервьюер: Здравствуйте, пожалуйста, сделайте краткое представление о себе.
домик: Меня зовут другой технический дом, я родом из ххх, и проекты, которые я сделал, в основном включают хххх с использованием технологий ххх, ххх.
интервьюер: Хорошо, у меня есть общее представление о вашем резюме, так что давайте сначала поговорим об основах.
домик: ОС сердца
Закуска
интервьюер: String, StringBuilder, StringBuffer разница?
домик: Это слишком просто, это смотрит на меня свысока?
- С точки зрения изменчивости String неизменяем, а длина StringBuilder и StringBuffer является переменной.
- С точки зрения скорости работы, StringBuilder > StringBuffer > String.
- С точки зрения безопасности потоков StringBuilder небезопасен для потоков, а StringBuffer — потокобезопасен.
So String: подходит для небольшого количества строковых операций, StringBuilder: подходит для большого количества операций в буфере символов в рамках одного потока, StringBuffer: подходит для большого количества операций в буфере символов в условиях многопоточности.
интервьюер: Почему String неизменяем?
домик: Поскольку массив символов, в котором хранятся данные, украшен финалом, он неизменяем.
интервьюер: Я только что сказал, что String неизменяем, но после выполнения следующего кода он изменился, почему так?
public class Demo {
public static void main(String[] args) {
String str = "不一样的";
str = str + "科技宅";
System.out.println(str);
}
}
Очевидно, что результатом выполнения вышеизложенного является:Технология - это не тот дом.
мы сначала используемjavac Demo.class
Скомпилировать, затем декомпилироватьjavap -verbose Demo
Получил следующий результат:
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: ldc #2 // String 不一样的
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: aload_1
11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: ldc #6 // String 科技宅
16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_1
23: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_1
27: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: return
Мы можем найти, что при использовании+
При сплайсинге jvm фактически инициализируетStringBuilder
сращенный. Эквивалентный скомпилированный код выглядит следующим образом:
public class Demo {
public static void main(String[] args) {
String str = "不一样的";
StringBuilder builder =new StringBuilder();
builder.append(str);
builder.append("科技宅");
str = builder.toString();
System.out.println(str);
}
}
мы можем посмотреть наbuilder.toString();
реализация.
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
Это понятноtoString
метод заключается в создании новогоString
объект вместо изменения старогоstr
содержание, эквивалентное размещению старогоstr
Ссылки на новыеString
объект. Этоstr
Причина изменения.
Поделитесь вопросом интервью, с которым я столкнулся, и вы можете догадаться, каков ответ?В конце статьи есть анализ
public class Demo {
public static void main(String[] args) {
String str = null;
str = str + "";
System.out.println(str);
}
}
интервьюер: Можно ли наследовать класс String?
домик: Нет, поскольку класс String изменяется с помощью ключевого слова final, он не может быть унаследован, а StringBuilder и StringBuffer также изменяются с помощью ключевого слова final.
интервьюер: Почему String Buffer является потокобезопасным?
домик: Это потому, что вStringBuffer
В классе используются обычно используемые методыsynchronized
Однако синхронизированный и, следовательно, потокобезопасныйStringBuilder
нисколько.这也就是运行速度StringBuilder
> StringBuffer
причина.
Интервьюер: Только что вы сказалиsynchronized
Ключевое слово, о котором можно говоритьsynchronized
форма выражения?
домик:
- Для обычных синхронизированных методов блокировкой является текущий объект экземпляра.
- Для статических синхронизированных методов блокировкой является объект класса текущего класса.
- Для синхронизированных блоков метода блокировка является объектом конфигурации синхронизированной скобки.
интервьюер: можно говоритьsynchronized
Принципиальная вещь?
домик:synchronized
это тяжеловесная блокировка, реализация зависит отJVM
изmonitor
Блокировка монитора. В основном используетсяmonitorenter
иmonitorexit
Директивы для реализации синхронизации методов и синхронизации блоков кода. При компиляции будетmonitorexit
Инструкция в начальной позиции блока кода синхронизации, иmonitorexit
Вставьте окончания методов и исключения, и каждыйmonitorexit
имеет соответствующийmonitorexit
.
Любой объект имеетmonitor
связанные с ним, когдаmonitor
После удержания он блокируется, и поток выполняется до тех пор, покаmonitorenter
время команды, он попытается получить соответствующийmonitor
Право собственности на приобретенный объект, то есть приобретение блокировки объекта, посколькуmonitorexit
Вставляется в конце метода и в исключении, поэтому блокировка автоматически снимается при выполнении метода или возникновении исключения.
приходят твердые овощи
интервьюер: Вы упомянули ранееsynchronized
Это тяжелый замок, вы знаете, как его оптимизировать?
домик: чтобы уменьшить потери производительности, вызванные получением и освобождением блокировок, для оптимизации вводятся смещенные блокировки, облегченные блокировки и тяжелые блокировки.Процесс обновления блокировки выглядит следующим образом:
Во-первых, это состояние без блокировки.Когда поток входит в блок кода синхронизации, он проверяет, сохранен ли идентификатор текущего потока в записи блокировки в заголовке объекта и во фрейме стека. не используетсяCAS
сделать замену. Потоку не нужно входить и выходить из блока синхронизированного кода позже.CAS
операция блокировки и разблокировки, нужно только судить заголовок объектаMark word
Сохранять ли смещенную блокировку, указывающую на текущий поток. Если yes указывает, что блокировка была получена, если нет или нет, вам нужно использоватьCAS
Замена, если настройка выполнена успешно, текущий поток удерживает блокировку смещения, в противном случае блокировка смещения будет отозвана и преобразована в упрощенную блокировку.
Процедура блокировки облегченных блокировок, блок синхронизации перед выполнением потока, кадр стека JVM создается в текущем пространстве блокировки потока для хранения записей, заголовка объекта иMark Word
Скопировать в заблокированную запись (Displaced Mark Word
), то поток пытается использоватьCAS
в заголовке объектаMark Word
Заменяется указателем на запись блокировки. В случае успеха текущий поток получает блокировку, в противном случае это означает, что другие потоки конкурируют за блокировку, и текущий поток пытается использовать вращение для получения блокировки.
Облегченный процесс разблокировки замка, при разблокировке CAS будет использоваться дляDisplaced Mark Word
Замените его обратно в заголовок объекта. Если это успешно, это означает, что конкуренция не произошла. В противном случае это означает, что нынешний замок расширит в тяжелый замок, если есть конкурирующая блокировка.
Схема процесса обновления
Простыми словами:
Возможно, приведенный выше процесс обновления и диаграмма процесса обновления немного сложны для понимания и немного запутаны. Мы можем сначала понять, почему существует процесс эскалации блокировок?HotSpotАвтор исследования обнаружил, что в большинстве случаев блокировка не только не конкурирует с несколькими потоками, но и всегда захватывается одним и тем же потоком несколько раз. Чтобы избежать потери производительности, вызванной получением и освобождением блокировок, вводится процесс эскалации блокировок. Понимание процесса эскалации блокировки требует ясности: блокировка будет обновлена и не может быть понижена до тех пор, пока не произойдет конкуренция.
Мы используем два потока T1, T2 для выполнения синхронизированного блока кода, чтобы продемонстрировать, как раздувается блокировка. Мы начинаем с состояния без блокировки.В это время T1 входит в блок кода синхронизации, чтобы определить состояние текущей блокировки. Выяснено, что это состояние без блокировки, которое будет использоваться в это время.CAS
Идентификатор потока в записи блокировки указывает на T1, а состояние отсутствия блокировки изменяется на смещенную блокировку. Поработав некоторое время, T2 вошел в блок кода синхронизации и обнаружил, что он уже смещен, поэтому я попытался использоватьCAS
Идите и попробуйте изменить идентификатор потока в записи блокировки на T2.Если изменение прошло успешно, T2 удерживает смещенную блокировку. Если это не удается, это означает, что есть конкуренция, и он модернизируется до облегченного замка.
可能你会有疑问,为啥会失败呢? Мы хотимCAS
начать операцию,CAS
Это сокращение от Compare-and-swap (сравните и замените), который является хорошо известным алгоритмом без блокировки. CAS должен иметь 3 операнда: адрес памяти V, старое ожидаемое значение A и целевое значение для обновления B. Другими словами, адрес памяти 0x01 хранит число 6, и я хочу превратить его в 7. В это время я сначала получаю значение 0x01 как 6, а затем снова получаю значение 0x01 и оцениваю, равно ли оно 6. Если это так, обновляю его до 7. Если нет, делаю это снова, пока оно не будет успешным. В основном это связано с квантами времени ЦП. Он может быть приостановлен на полпути выполнения, а затем другие потоки изменяют значение. В это время программа может установить неправильное значение, что приведет к ненормальным результатам.
Легко понятьCAS
Теперь давайте вернемся к процессу эскалации блокировки, T2 пытается использоватьCAS
Заменив идентификатор потока в записи блокировки, результатCAS
Failed, значит, в это время T1 украл замок, который изначально принадлежал T2 Очевидно, этот момент случился.конкурироватьТак что замок надо модернизировать. Перед обновлением до облегченной блокировки смещенный поток T1 будет приостановлен и проверит состояние T1, если T1Неактивное состояние/выход из синхронизированного блока, T1 снимет блокировку смещения и выйдет из спящего режима. Если блок кода синхронизации не вышел, в это время он будет обновлен до облегченной блокировки, и блокировка будет получена T1, и выполнение продолжится с безопасной точки, а облегченная блокировка будет освобождена после выполнения.
Смещенная блокировка использует механизм освобождения блокировки после конкуренции, поэтому, когда другие потоки пытаются конкурировать за смещенную блокировку, поток, удерживающий смещенную блокировку, освобождает блокировку. А для отмены предвзятых блокировок требуется ожидание глобальной точки сохранения (в этот момент байт-код не выполняется).
T1 какое-то время работает без сбоев, потому что никто не соревнуется. В определенный момент времени появляется T2 и используетCAS
Блокировка получена, но обнаруживается, что она терпит неудачу. В это время T2 будет ждать некоторое время (поверните, чтобы получить блокировку). Поскольку конкуренция не очень жесткая, после завершения выполнения T1 блокировка может получить и выполнить. Если замок не может быть получен в течение длительного времени, может возникнуть конкуренция.Там может быть T3, который крадет легкий замок, который изначально принадлежал T2, и в это время он будет повышен до тяжелого замка.
отступить после еды
интервьюер:Inner OS: Я его не спрашивал, похоже, он и не надеется, пусть возвращается и делает уведомления.
Сяочжай, верно? У меня есть общее представление о вашем уровне здесь, и я вполне доволен вами. Однако на нашей стороне все еще есть несколько кандидатов, которые еще не прошли собеседование, поэтому мы не можем дать вам прямой ответ. следует сначала вернуться и дождаться уведомления.
домик: Хорошо, спасибо, интервьюер, я сначала вернусь сюда. Благодаря моей полной подготовке я ответил на все ответы, и я должен быть в состоянии получить предложение.
Анализ вопросов интервью.
public class Demo {
public static void main(String[] args) {
String str = null;
str = str + "";
System.out.println(str);
}
}
ответnull, ранее мы узнали, что использование+
Выполнение сплайсинга фактически преобразуется вStringBuilder
использоватьappend
способ сращивания. Итак, давайте посмотримappend
Логика реализации метода понятна.
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
private AbstractStringBuilder appendNull() {
int c = count;
ensureCapacityInternal(c + 4);
final char[] value = this.value;
value[c++] = 'n';
value[c++] = 'u';
value[c++] = 'l';
value[c++] = 'l';
count = c;
return this;
}
Как видно из кода, если входящая строкаnull
при звонкеappendNull
метод, в то время какappendNull
вернет ноль.
конец
Я другой технарь, каждый день я делаю небольшие успехи и живу другой жизнью. Увидимся в следующий раз!
Если вы считаете, что это полезно для вас, вы можете прокомментировать и поставить лайк, или вы можете перейти на мою домашнюю страницу, чтобы увидеть, может быть, есть статья, которая вам нравится, вы также можете просто подписаться на нее, спасибо.