Кто-нибудь может сказать мне преимущество синхронизированного метода перед синхронизированным блоком с примером?
Кто-нибудь может сказать мне преимущество синхронизированного метода перед синхронизированным блоком с примером?
Ответы:
Может кто-нибудь сказать мне преимущество синхронизированного метода над синхронизированным блоком на примере? Спасибо.
Нет явного преимущества использования синхронизированного метода над блоком.
Возможно, единственным (но я бы не назвал это преимуществом) является то, что вам не нужно включать ссылку на объект this
.
Метод:
public synchronized void method() { // blocks "this" from here....
...
...
...
} // to here
Блок:
public void method() {
synchronized( this ) { // blocks "this" from here ....
....
....
....
} // to here...
}
Видеть? Никаких преимуществ вообще.
Блоки действительно имеют преимущество по сравнению с методами , хотя, в основном , в гибкости , потому что вы можете использовать другой объект , как замок в то время как метод синхронизация будет блокировать весь объект.
Для сравнения:
// locks the whole object
...
private synchronized void someInputRelatedWork() {
...
}
private synchronized void someOutputRelatedWork() {
...
}
против
// Using specific locks
Object inputLock = new Object();
Object outputLock = new Object();
private void someInputRelatedWork() {
synchronized(inputLock) {
...
}
}
private void someOutputRelatedWork() {
synchronized(outputLock) {
...
}
}
Также, если метод растет, вы все равно можете держать синхронизированный раздел отделенным:
private void method() {
... code here
... code here
... code here
synchronized( lock ) {
... very few lines of code here
}
... code here
... code here
... code here
... code here
}
synchronized
Блок осуществляется с помощью двух команд, monitorenter
и monitorexit
, плюс обработчик исключений , который гарантирует , что monitorexit
называется даже в исключительном случае. Это все сохраняется при использовании synchronized
метода.
Единственная реальная разница в том, что синхронизированный блок может выбирать, на каком объекте он синхронизируется. Синхронизированный метод может использовать только 'this'
(или соответствующий экземпляр класса для синхронизированного метода класса). Например, они семантически эквивалентны:
synchronized void foo() {
...
}
void foo() {
synchronized (this) {
...
}
}
Последний является более гибким, поскольку он может конкурировать за связанную блокировку любого объекта, часто переменной-члена. Это также более детально, потому что у вас может быть параллельный код, выполняющийся до и после блока, но все же внутри метода. Конечно, вы могли бы также легко использовать синхронизированный метод путем рефакторинга параллельного кода в отдельные несинхронизированные методы. Используйте то, что делает код более понятным.
Плюсы:
Минусы:
Плюсы:
Минусы:
Лично я предпочитаю использовать синхронизированные методы с классами, ориентированными только на то, что требует синхронизации. Такой класс должен быть как можно меньше, и поэтому должно быть легко проверить синхронизацию. Другим не нужно заботиться о синхронизации.
Основное отличие состоит в том, что если вы используете синхронизированный блок, вы можете заблокировать объект, отличный от этого, что позволяет быть более гибким.
Предположим, у вас есть очередь сообщений и несколько производителей и потребителей сообщений. Мы не хотим, чтобы производители вмешивались друг в друга, но потребители должны иметь возможность получать сообщения, не ожидая производителей. Итак, мы просто создаем объект
Object writeLock = new Object();
И теперь каждый раз, когда производители хотят добавить новое сообщение, мы просто фиксируем это:
synchronized(writeLock){
// do something
}
Таким образом, потребители все еще могут читать, а производители будут заблокированы.
Синхронизированный метод
Синхронизированные методы имеют два эффекта.
Во-первых, когда один поток выполняет синхронизированный метод для объекта, все остальные потоки, которые вызывают синхронизированные методы для того же блока объекта (приостанавливают выполнение), пока первый поток не завершится с объектом.
Во-вторых, при выходе из синхронизированного метода он автоматически устанавливает отношение «до и после» с любым последующим вызовом синхронизированного метода для того же объекта. Это гарантирует, что изменения состояния объекта видны всем потокам.
Обратите внимание, что конструкторы не могут быть синхронизированы - использование ключевого слова synchronized с конструктором является синтаксической ошибкой. Синхронизация конструкторов не имеет смысла, потому что только поток, который создает объект, должен иметь доступ к нему во время его конструирования.
Синхронизированный оператор
В отличие от синхронизированных методов, синхронизированные операторы должны указывать объект, который обеспечивает внутреннюю блокировку: чаще всего я использую это для синхронизации доступа к списку или карте, но я не хочу блокировать доступ ко всем методам объекта.
Q: Внутренние блокировки и синхронизация Синхронизация строится вокруг внутренней сущности, известной как внутренняя блокировка или блокировка монитора. (Спецификация API часто именует эту сущность просто как «монитор».) Внутренние блокировки играют роль в обоих аспектах синхронизации: обеспечение исключительного доступа к состоянию объекта и установление отношений «до и после», которые важны для видимости.
Каждый объект имеет встроенную блокировку, связанную с ним. По соглашению поток, которому требуется исключительный и согласованный доступ к полям объекта, должен получить внутреннюю блокировку объекта перед тем, как получить к ним доступ, а затем снять внутреннюю блокировку, когда это будет сделано с ними. Говорят, что поток владеет внутренней блокировкой между моментом, когда он получил блокировку и снял ее. Пока потоку принадлежит внутренняя блокировка, никакой другой поток не может получить такую же блокировку. Другой поток заблокируется, когда попытается получить блокировку.
package test;
public class SynchTest implements Runnable {
private int c = 0;
public static void main(String[] args) {
new SynchTest().test();
}
public void test() {
// Create the object with the run() method
Runnable runnable = new SynchTest();
Runnable runnable2 = new SynchTest();
// Create the thread supplying it with the runnable object
Thread thread = new Thread(runnable,"thread-1");
Thread thread2 = new Thread(runnable,"thread-2");
// Here the key point is passing same object, if you pass runnable2 for thread2,
// then its not applicable for synchronization test and that wont give expected
// output Synchronization method means "it is not possible for two invocations
// of synchronized methods on the same object to interleave"
// Start the thread
thread.start();
thread2.start();
}
public synchronized void increment() {
System.out.println("Begin thread " + Thread.currentThread().getName());
System.out.println(this.hashCode() + "Value of C = " + c);
// If we uncomment this for synchronized block, then the result would be different
// synchronized(this) {
for (int i = 0; i < 9999999; i++) {
c += i;
}
// }
System.out.println("End thread " + Thread.currentThread().getName());
}
// public synchronized void decrement() {
// System.out.println("Decrement " + Thread.currentThread().getName());
// }
public int value() {
return c;
}
@Override
public void run() {
this.increment();
}
}
Перепроверьте различные выходы с синхронизированным методом, блоком и без синхронизации.
Примечание: статические синхронизированные методы и блоки работают с объектом Class.
public class MyClass {
// locks MyClass.class
public static synchronized void foo() {
// do something
}
// similar
public static void foo() {
synchronized(MyClass.class) {
// do something
}
}
}
Когда компилятор Java преобразует ваш исходный код в байт-код, он обрабатывает синхронизированные методы и синхронизированные блоки совершенно по-разному.
Когда JVM выполняет синхронизированный метод, исполняющий поток идентифицирует, что в структуре method_info метода установлен флаг ACC_SYNCHRONIZED, затем он автоматически получает блокировку объекта, вызывает метод и снимает блокировку. Если возникает исключение, поток автоматически снимает блокировку.
Синхронизация блока метода, с другой стороны, обходит встроенную поддержку JVM для получения блокировки объекта и обработки исключений и требует, чтобы функциональность была явно записана в байтовом коде. Если вы прочитаете байт-код для метода с синхронизированным блоком, вы увидите более десятка дополнительных операций для управления этой функциональностью.
Здесь показаны вызовы для генерации синхронизированного метода и синхронизированного блока:
public class SynchronizationExample {
private int i;
public synchronized int synchronizedMethodGet() {
return i;
}
public int synchronizedBlockGet() {
synchronized( this ) {
return i;
}
}
}
synchronizedMethodGet()
Метод генерирует следующий байт - код:
0: aload_0
1: getfield
2: nop
3: iconst_m1
4: ireturn
И вот байт-код из synchronizedBlockGet()
метода:
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: aload_0
5: getfield
6: nop
7: iconst_m1
8: aload_1
9: monitorexit
10: ireturn
11: astore_2
12: aload_1
13: monitorexit
14: aload_2
15: athrow
Одно существенное различие между синхронизированным методом и блоком состоит в том, что синхронизированный блок обычно уменьшает область блокировки. Поскольку объем блокировки обратно пропорционален производительности, всегда лучше блокировать только критическую часть кода. Один из лучших примеров использования синхронизированного блока - это двойная проверка блокировки в шаблоне Singleton, где вместо блокировки целого getInstance()
метода мы блокируем только критическую часть кода, которая используется для создания экземпляра Singleton. Это резко повышает производительность, потому что блокировка требуется только один или два раза.
При использовании синхронизированных методов вам нужно будет проявлять особую осторожность, если вы смешиваете статические синхронизированные и нестатические синхронизированные методы.
monitorenter
и monitorexit
перед запуском кода.
Чаще всего я использую это для синхронизации доступа к списку или карте, но я не хочу блокировать доступ ко всем методам объекта.
В следующем коде один поток, модифицирующий список, не будет блокировать ожидание потока, который модифицирует карту. Если бы методы были синхронизированы на объекте, то каждый метод должен был бы ждать, даже если изменения, которые они делают, не будут конфликтовать.
private List<Foo> myList = new ArrayList<Foo>();
private Map<String,Bar) myMap = new HashMap<String,Bar>();
public void put( String s, Bar b ) {
synchronized( myMap ) {
myMap.put( s,b );
// then some thing that may take a while like a database access or RPC or notifying listeners
}
}
public void hasKey( String s, ) {
synchronized( myMap ) {
myMap.hasKey( s );
}
}
public void add( Foo f ) {
synchronized( myList ) {
myList.add( f );
// then some thing that may take a while like a database access or RPC or notifying listeners
}
}
public Thing getMedianFoo() {
Foo med = null;
synchronized( myList ) {
Collections.sort(myList);
med = myList.get(myList.size()/2);
}
return med;
}
С синхронизированными блоками у вас может быть несколько синхронизаторов, так что одновременно может происходить несколько одновременных, но не конфликтующих вещей.
Синхронизированные методы можно проверить с помощью API отражения. Это может быть полезно для тестирования некоторых контрактов, например, все методы в модели синхронизированы .
Следующий фрагмент выводит все синхронизированные методы Hashtable:
for (Method m : Hashtable.class.getMethods()) {
if (Modifier.isSynchronized(m.getModifiers())) {
System.out.println(m);
}
}
Важное замечание по использованию синхронизированного блока: осторожно, что вы используете в качестве объекта блокировки!
Фрагмент кода из user2277816 выше иллюстрирует этот момент в том смысле, что ссылка на строковый литерал используется в качестве объекта блокировки. Поймите, что строковые литералы автоматически внедряются в Java, и вы должны начать видеть проблему: каждый фрагмент кода, который синхронизируется с литералом «блокировка», разделяет одну и ту же блокировку! Это может легко привести к взаимоблокировке с совершенно не связанными частями кода.
С объектами нужно быть осторожнее. Примитивы в штучной упаковке также представляют опасность, поскольку методы autoboxing и valueOf могут повторно использовать одни и те же объекты в зависимости от значения.
Для получения дополнительной информации см .: https://www.securecoding.cert.org/confluence/display/java/LCK01-J.+Do+not+synchronize+on+objects+that+may+be+reused
Часто использование блокировки на уровне метода слишком грубо. Зачем блокировать кусок кода, который не имеет доступа к каким-либо общим ресурсам, блокируя весь метод. Поскольку каждый объект имеет блокировку, вы можете создавать фиктивные объекты для реализации синхронизации на уровне блоков. Уровень блока более эффективен, потому что он не блокирует весь метод.
Вот пример
Уровень метода
class MethodLevel {
//shared among threads
SharedResource x, y ;
public void synchronized method1() {
//multiple threads can't access
}
public void synchronized method2() {
//multiple threads can't access
}
public void method3() {
//not synchronized
//multiple threads can access
}
}
Уровень блока
class BlockLevel {
//shared among threads
SharedResource x, y ;
//dummy objects for locking
Object xLock = new Object();
Object yLock = new Object();
public void method1() {
synchronized(xLock){
//access x here. thread safe
}
//do something here but don't use SharedResource x, y
// because will not be thread-safe
synchronized(xLock) {
synchronized(yLock) {
//access x,y here. thread safe
}
}
//do something here but don't use SharedResource x, y
//because will not be thread-safe
}//end of method1
}
[Редактировать]
Для Collection
как Vector
и Hashtable
они синхронизированы , когда ArrayList
или HashMap
нет , и вы должны набора синхронизированы ключевым слово или запускайте Collections синхронизированного метода:
Map myMap = Collections.synchronizedMap (myMap); // single lock for the entire map
List myList = Collections.synchronizedList (myList); // single lock for the entire list
Единственное отличие: синхронизированные блоки допускают гранулярную блокировку в отличие от синхронизированного метода
В основном synchronized
блок или методы использовались для написания поточно-безопасного кода, избегая ошибок несогласованности памяти.
Этот вопрос очень старый и многое изменилось за последние 7 лет. Новые конструкции программирования были введены для обеспечения безопасности потоков.
Вы можете достичь безопасности потоков, используя расширенный API параллелизма вместо synchronied
блоков. На этой странице документации представлены хорошие программные конструкции для обеспечения безопасности потоков.
Объекты блокировки поддерживают идиомы блокировки, которые упрощают многие параллельные приложения.
Исполнители определяют высокоуровневый API для запуска и управления потоками. Реализации исполнителя, предоставляемые java.util.concurrent, обеспечивают управление пулом потоков, подходящее для крупномасштабных приложений.
Параллельные сборы упрощают управление большими коллекциями данных и могут значительно сократить потребность в синхронизации.
Атомарные переменные имеют функции, которые минимизируют синхронизацию и помогают избежать ошибок согласованности памяти.
ThreadLocalRandom (в JDK 7) обеспечивает эффективную генерацию псевдослучайных чисел из нескольких потоков.
Лучшая замена синхронизированным - ReentrantLock , который использует Lock
API
Блокировка взаимного исключения с повторным входом с тем же базовым поведением и семантикой, что и для неявной блокировки монитора, доступ к которой осуществляется с помощью синхронизированных методов и операторов, но с расширенными возможностями.
Пример с замками:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
См. Также пакеты java.util.concurrent и java.util.concurrent.atomic для других программных конструкций.
См. Также этот связанный вопрос:
В общем, это в основном то же самое, кроме того, что они явно относятся к монитору объекта, который используется против неявного объекта this. Недостатком синхронизированных методов, который, я думаю, иногда упускают из виду, является то, что при использовании ссылки this для синхронизации вы оставляете открытой возможность блокировки внешних объектов на одном и том же объекте. Это может быть очень тонкая ошибка, если вы столкнетесь с ней. Синхронизация по внутреннему явному объекту или другому существующему полю может избежать этой проблемы, полностью инкапсулируя синхронизацию.
Как уже было сказано, синхронизированный блок может использовать пользовательскую переменную в качестве объекта блокировки, когда синхронизированная функция использует только «this». И, конечно, вы можете манипулировать областями своей функции, которые должны быть синхронизированы. Но все говорят, что нет никакой разницы между синхронизированной функцией и блоком, который покрывает всю функцию, используя «this» в качестве объекта блокировки. Это не так, разница в байт-коде, который будет сгенерирован в обеих ситуациях. В случае использования синхронизированного блока должна быть выделена локальная переменная, которая содержит ссылку на «это». И как результат, у нас будет немного больший размер для функции (не имеет значения, если у вас всего несколько функций).
Более подробное объяснение разницы вы можете найти здесь: http://www.artima.com/insidejvm/ed2/threadsynchP.html
В случае синхронизированных методов, блокировка будет получена на объекте. Но если вы используете синхронизированный блок, у вас есть возможность указать объект, для которого будет получена блокировка.
Пример :
Class Example {
String test = "abc";
// lock will be acquired on String test object.
synchronized (test) {
// do something
}
lock will be acquired on Example Object
public synchronized void testMethod() {
// do some thing
}
}
Я знаю, что это старый вопрос, но, прочитав ответы здесь, я не заметил, чтобы кто-то упомянул, что иногда synchronized
метод может быть неправильной блокировкой.
Из Java-параллелизма на практике (стр. 72):
public class ListHelper<E> {
public List<E> list = Collections.syncrhonizedList(new ArrayList<>());
...
public syncrhonized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
Приведенный выше код выглядит как поточно-ориентированный. Однако на самом деле это не так. В этом случае блокировка получается на экземпляре класса. Однако возможно изменение списка другим потоком, не использующим этот метод. Правильный подход будет использовать
public boolean putIfAbsent(E x) {
synchronized(list) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
}
Приведенный выше код блокирует все потоки, пытающиеся изменить список, от изменения списка до завершения синхронизированного блока.
List
может привести к проблемам с производительностью, если существует журнал кода, который не обязательно должен быть синхронизирован
На практике преимущество синхронизированных методов перед синхронизированными блоками заключается в том, что они более устойчивы к идиотам; поскольку вы не можете выбрать произвольный объект для блокировки, вы не можете неправильно использовать синтаксис синхронизированного метода, чтобы делать глупые вещи, такие как блокировка строкового литерала или блокировка содержимого изменяемого поля, которое изменяется из-под потоков.
С другой стороны, с синхронизированными методами вы не можете защитить блокировку от получения любым потоком, который может получить ссылку на объект.
Таким образом, использование синхронизированного в качестве модификатора в методах лучше для защиты ваших сотрудников-коров от вреда для себя, в то время как использование синхронизированных блоков в сочетании с частными объектами окончательной блокировки лучше для защиты вашего собственного кода от сотрудников-коров.
Из резюме спецификации Java: http://www.cs.cornell.edu/andru/javaspec/17.doc.html
Синхронизированный оператор (§14.17) вычисляет ссылку на объект; затем он пытается выполнить действие блокировки для этого объекта и не продолжается, пока действие блокировки не будет успешно завершено. ...
Синхронизированный метод (§8.4.3.5) автоматически выполняет действие блокировки при его вызове; его тело не будет выполнено, пока действие блокировки не будет успешно завершено. Если метод является методом экземпляра , он блокирует блокировку, связанную с экземпляром, для которого он был вызван (то есть объектом, который будет известен как этот во время выполнения тела метода). Если метод является статическим , он блокирует блокировку, связанную с объектом Class, который представляет класс, в котором определен метод. ...
Основываясь на этих описаниях, я бы сказал, что большинство предыдущих ответов верны, и синхронизированный метод может быть особенно полезен для статических методов, где в противном случае вам пришлось бы выяснить, как получить объект «Class», представляющий класс, в котором этот метод был определены «.
Изменить: я первоначально думал, что это были цитаты из фактической спецификации Java. Уточнил, что эта страница является лишь кратким описанием / объяснением спецификации
TLDR; Ни используйте synchronized
модификатор, ни synchronized(this){...}
выражение, но synchronized(myLock){...}
где myLock
находится конечное поле экземпляра, содержащее закрытый объект.
Разница между использованием synchronized
модификатора в объявлении метода и synchronized(..){ }
выражением в теле метода заключается в следующем:
synchronized
Модификатор указанный на подписи методы
synchronized(this) { .... }
, иthis
объект в качестве блокировки при объявлении нестатического метода или включающего класса при объявлении статического метода.synchronized(...){...}
Выражение позволяет
Однако использование synchronized
модификатора или synchronized(...) {...}
с использованием this
в качестве объекта блокировки (как в synchronized(this) {...}
) имеет тот же недостаток. Оба используют свой собственный экземпляр в качестве объекта блокировки для синхронизации. Это опасно, потому что не только сам объект, но и любой другой внешний объект / код, который содержит ссылку на этот объект, также может использовать его в качестве блокировки синхронизации с потенциально серьезными побочными эффектами (снижение производительности и взаимоблокировки ).
Поэтому рекомендуется не использовать ни synchronized
модификатор, ни synchronized(...)
выражение в сочетании с this
объектом блокировки, а с объектом блокировки, закрытым для этого объекта. Например:
public class MyService {
private final lock = new Object();
public void doThis() {
synchronized(lock) {
// do code that requires synchronous execution
}
}
public void doThat() {
synchronized(lock) {
// do code that requires synchronous execution
}
}
}
Вы также можете использовать несколько объектов блокировки, но необходимо соблюдать особую осторожность, чтобы гарантировать, что это не приведет к взаимным блокировкам при использовании вложенных.
public class MyService {
private final lock1 = new Object();
private final lock2 = new Object();
public void doThis() {
synchronized(lock1) {
synchronized(lock2) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThat() and doMore().
}
}
public void doThat() {
synchronized(lock1) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThis().
// doMore() may execute concurrently
}
}
public void doMore() {
synchronized(lock2) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThis().
// doThat() may execute concurrently
}
}
}
Я предполагаю, что этот вопрос о разнице между Thread Safe Singleton и Lazy инициализацией с двойной проверкой блокировки . Я всегда ссылаюсь на эту статью, когда мне нужно реализовать какой-то конкретный синглтон.
Ну, это потокобезопасный синглтон :
// Java program to create Thread Safe
// Singleton class
public class GFG
{
// private instance, so that it can be
// accessed by only by getInstance() method
private static GFG instance;
private GFG()
{
// private constructor
}
//synchronized method to control simultaneous access
synchronized public static GFG getInstance()
{
if (instance == null)
{
// if instance is null, initialize
instance = new GFG();
}
return instance;
}
}
Плюсы:
Ленивая инициализация возможна.
Это потокобезопасно.
Минусы:
- Метод getInstance () синхронизирован, поэтому он снижает производительность, так как несколько потоков не могут получить к нему доступ одновременно.
Это Ленивая инициализация с двойной проверкой блокировки :
// Java code to explain double check locking
public class GFG
{
// private instance, so that it can be
// accessed by only by getInstance() method
private static GFG instance;
private GFG()
{
// private constructor
}
public static GFG getInstance()
{
if (instance == null)
{
//synchronized block to remove overhead
synchronized (GFG.class)
{
if(instance==null)
{
// if instance is null, initialize
instance = new GFG();
}
}
}
return instance;
}
}
Плюсы:
Ленивая инициализация возможна.
Это также потокобезопасный.
Производительность снижена из-за синхронизации синхронизированного ключевого слова.
Минусы:
В первый раз это может повлиять на производительность.
Как минусы. Метод двойной проверки блокировки сносен, поэтому его можно использовать для высокопроизводительных многопоточных приложений.
Пожалуйста, обратитесь к этой статье для более подробной информации:
https://www.geeksforgeeks.org/java-singleton-design-pattern-practices-examples/
Синхронизация с потоками. 1) НИКОГДА не используйте синхронизированный (это) в потоке, он не работает. Синхронизация с (this) использует текущий поток в качестве объекта блокировки потока. Поскольку каждый поток не зависит от других потоков, координация синхронизации отсутствует. 2) Тесты кода показывают, что в Java 1.6 на Mac не работает синхронизация методов. 3) synchronized (lockObj), где lockObj - это общий общий объект всех синхронизирующихся на нем потоков. 4) ReenterantLock.lock () и .unlock () работают. Смотрите Java уроки для этого.
Следующий код показывает эти пункты. Он также содержит потокобезопасный вектор, который будет заменен на ArrayList, чтобы показать, что многие потоки, добавляемые в вектор, не теряют никакой информации, в то время как тот же самый с ArrayList может потерять информацию. 0) Текущий код показывает потерю информации из-за состояния гонки. A) Прокомментируйте текущую помеченную строку A и раскомментируйте строку A над ней, затем запустите, метод теряет данные, но это не должно. B) Обратный шаг A, раскомментируйте B и // конец блока}. Затем запустите, чтобы увидеть результаты без потери данных. C) Закомментируйте B, раскомментируйте C. Выполните, посмотрите, как синхронизируется (это) потеря данных, как и ожидалось. Не успеваю завершить все варианты, надеюсь, это поможет. Если выполняется синхронизация по (этому) или метод синхронизации работает, укажите, какую версию Java и ОС вы тестировали. Спасибо.
import java.util.*;
/** RaceCondition - Shows that when multiple threads compete for resources
thread one may grab the resource expecting to update a particular
area but is removed from the CPU before finishing. Thread one still
points to that resource. Then thread two grabs that resource and
completes the update. Then thread one gets to complete the update,
which over writes thread two's work.
DEMO: 1) Run as is - see missing counts from race condition, Run severa times, values change
2) Uncomment "synchronized(countLock){ }" - see counts work
Synchronized creates a lock on that block of code, no other threads can
execute code within a block that another thread has a lock.
3) Comment ArrayList, unComment Vector - See no loss in collection
Vectors work like ArrayList, but Vectors are "Thread Safe"
May use this code as long as attribution to the author remains intact.
/mf
*/
public class RaceCondition {
private ArrayList<Integer> raceList = new ArrayList<Integer>(); // simple add(#)
// private Vector<Integer> raceList = new Vector<Integer>(); // simple add(#)
private String countLock="lock"; // Object use for locking the raceCount
private int raceCount = 0; // simple add 1 to this counter
private int MAX = 10000; // Do this 10,000 times
private int NUM_THREADS = 100; // Create 100 threads
public static void main(String [] args) {
new RaceCondition();
}
public RaceCondition() {
ArrayList<Thread> arT = new ArrayList<Thread>();
// Create thread objects, add them to an array list
for( int i=0; i<NUM_THREADS; i++){
Thread rt = new RaceThread( ); // i );
arT.add( rt );
}
// Start all object at once.
for( Thread rt : arT ){
rt.start();
}
// Wait for all threads to finish before we can print totals created by threads
for( int i=0; i<NUM_THREADS; i++){
try { arT.get(i).join(); }
catch( InterruptedException ie ) { System.out.println("Interrupted thread "+i); }
}
// All threads finished, print the summary information.
// (Try to print this informaiton without the join loop above)
System.out.printf("\nRace condition, should have %,d. Really have %,d in array, and count of %,d.\n",
MAX*NUM_THREADS, raceList.size(), raceCount );
System.out.printf("Array lost %,d. Count lost %,d\n",
MAX*NUM_THREADS-raceList.size(), MAX*NUM_THREADS-raceCount );
} // end RaceCondition constructor
class RaceThread extends Thread {
public void run() {
for ( int i=0; i<MAX; i++){
try {
update( i );
} // These catches show when one thread steps on another's values
catch( ArrayIndexOutOfBoundsException ai ){ System.out.print("A"); }
catch( OutOfMemoryError oome ) { System.out.print("O"); }
}
}
// so we don't lose counts, need to synchronize on some object, not primitive
// Created "countLock" to show how this can work.
// Comment out the synchronized and ending {, see that we lose counts.
// public synchronized void update(int i){ // use A
public void update(int i){ // remove this when adding A
// synchronized(countLock){ // or B
// synchronized(this){ // or C
raceCount = raceCount + 1;
raceList.add( i ); // use Vector
// } // end block for B or C
} // end update
} // end RaceThread inner class
} // end RaceCondition outter class