синхронизация, отличная от текущего объекта

2

У меня есть класс, называемый counter, в этом экземпляре stringbuffer, а целочисленная переменная делится на два потока.

мой вопрос здесь в классе счетчиков, я использовал синхронизированный (буфер), буфер - это не что иное, как экземпляр stringbuffer.

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

class Counter implements Runnable
{

  public StringBuffer buffer = new StringBuffer();
  public int cnt = 0;

  @Override
  public void run() {
    synchronized (buffer)
     {
        for (int i = 0; i < 100; i++) 
        {
            cnt++;
        }
    }
  }
}


public class Test {
  public static void main(String[] args) {
    Counter counter = new Counter(); 
    Thread t1 = new Thread(counter);
    Thread t2 = new Thread(counter);
    t1.start();
    t2.start();
  }
}
Теги:
multithreading
synchronization

3 ответа

4

На самом деле здесь не так много сказать; странно, что мне потребовалось 10+ минут, чтобы записать мой ответ.

Видите ли, один из двух потоков сначала получает замок; то он считается равным 100; затем другой берет замок; и дополнительно увеличивает счетчик до 200.

И в вашем случае это действительно не имеет никакого значения, если есть

synchronized(this)

или же

synchronized(buffer)

Дело в том, что в обоих случаях "этот" и "буфер" приводит к "той же" ссылке. Понимаете, в вашем примере есть только один объект Counter; поэтому, когда эти потоки называют метод run; "counter.this"; и "counter.buffer" имеют тот же эффект: два потока будут синхронизироваться с одним объектом.

Чтобы получить разные результаты, вы можете переделать свой пример:

public static StringBuffer buffer = new StringBuffer();
public static int ...

И затем используйте два объекта-счетчика:

Counter counter1 = new Counter(); 
Counter counter2 = new Counter(); 

Thread t1 = new Thread(counter1);
Thread t2 = new Thread(counter2);

Когда вы это сделаете, вы обнаружите, что синхронизация (буфер) по-прежнему дает вам 200 в конце; тогда как синхронизация (это) может дать вам все возможные результаты.

Потому что теперь counter1.this и counter2.this не являются одним и тем же объектом; тогда как counter1.buffer и counter2.buffer есть!

Другими словами: когда разные потоки "обновляют" один и тот же объект; то они должны синхронизировать одну и ту же блокировку. Потому что, если они синхронизируются с разными объектами блокировки; сюрприз - тогда нет блокировки, нет синхронизации; и поэтому: случайные записи параллельно, с известным результатом непредсказуемости!

0

Разница становится яснее, если в вашем class имеется более одного общего ресурса. Вы можете расширить свой пример до двух counters. Теперь блокировка на this будет означать, что countA и countB не могут выполняться разными threads одновременно, хотя эти методы независимы. Если у вас есть блокировка для каждого ресурса, вы гарантируете, что countA и countB не могут выполняться одновременно. По-прежнему возможно, что один thread выполняет countA и другой countB одновременно.

class Counter implements Runnable {

    private StringBuffer lockA = new StringBuffer();
    private int cntA = 0;
    private StringBuffer lockB = new StringBuffer();
    private int cntB = 0;

    public void countA() {
        synchronized (lockA) {
            for (int i = 0; i < 100; i++) {
                cntA++;
            }
            System.out.println("countA:" + cntA);
        }

    }

    public void countB() {
        synchronized (lockB) {
            for (int i = 0; i < 100; i++) {
                cntB++;
            }
            System.out.println("countB:" + cntB);
        }

    }

    @Override
    public void run() {
        countA();
        countB();
    }
}
  • 0
    все еще сбивает с толку, можете ли вы привести любой пример, связанный с jdbc или коллекциями
0

Одно распространенное недоразумение, которое люди имеют о встроенных замках, - это идея о том, что приобретение блокировки делает что-то для защиты свойств объекта. Если это то, о чем вы думаете, это не так.

Каждый объект может получить свой замок, но это не влияет на остальную часть объекта. В этом примере есть один Runnable, который разделяется между потоками, и что Runnable имеет один StringBuffer. Так до тех пор, пока существует один замок, который является общим, это не имеет никакого значения, является ли эта блокировка на this (в Runnable) или на StringBuffer, или на объекте специального замка (означающее элемент экземпляра private final Object LOCK = new Object().

Единственная возможная проблема, с которой объект блокируется (что может быть связано с большими программами с несколькими потоками), - это объем доступности блокировки. Существует аргумент, что использование this для блокировки может быть плохим из-за того, что блокировка доступна для других потоков, которые также могут получить блокировку на ней. Уменьшение объема блокировки для частного члена экземпляра (StringBuffer или выделенного объекта блокировки) облегчает рассуждение о том, кто может получить блокировку.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню