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

Я знаю, что использование ключевого слова synchronize до того, как метод приносит синхронизацию с этим объектом.
То есть будут синхронизированы 2 потока, запускающих один и тот же экземпляр объекта.
Однако, поскольку синхронизация находится на уровне объекта, 2 потока, выполняющие разные экземпляры объекта, не будут синхронизированы. Итак, если у нас есть статическая переменная в классе java, которая вызывается методом, мы бы хотели, чтобы она была синхронизирована между экземплярами класса.
Два экземпляра работают в двух разных потоках.
Можем ли мы добиться синхронизации следующим образом?

public class Test  
{  
   private static int count = 0;  
   private static final Object lock= new Object();    
   public synchronized void foo() 
  {  
      synchronized(lock)
     {  
         count++;  
     }  
  }  
}

Верно ли, что, поскольку мы определили объект "блокировка", который является статичным, и мы используем ключевое слово, синхронизированное для этой блокировки, статический счет varibale теперь синхронизируется между экземплярами класса Test?

Ответ 1

Существует несколько способов синхронизации доступа к статической переменной.

  • Использовать синхронизированный статический метод. Это синхронизирует объект класса.

    public class Test {
        private static int count = 0;
    
        public static synchronized void incrementCount() {
            count++;
        }
    } 
    
  • Явная синхронизация объекта класса.

    public class Test {
        private static int count = 0;
    
        public void incrementCount() {
            synchronized (Test.class) {
                count++;
            }
        }
    } 
    
  • Синхронизировать другой статический объект.

    public class Test {
        private static int count = 0;
        private static final Object countLock = new Object();
    
        public void incrementCount() {
            synchronized (countLock) {
                count++;
            }
        }
    } 
    

Метод 3 является лучшим во многих случаях, поскольку объект блокировки не отображается вне вашего класса.

Ответ 2

Если вы просто используете счетчик, подумайте об использовании AtomicInteger или другого подходящего класса из пакета java.util.concurrent.atomic:

public class Test {

    private final static AtomicInteger count = new AtomicInteger(0); 

    public void foo() {  
        count.incrementAndGet();
    }  
}

Ответ 3

Да, это правда.

Если вы создаете два экземпляра своего класса

Test t1 = new Test();
Test t2 = new Test();

Затем t1.foo и t2.foo синхронизируются на одном и том же статическом объекте и, следовательно, блокируют друг друга.

Ответ 4

Вы можете синхронизировать свой код над классом. Это было бы проще.

   public class Test  
    {  
       private static int count = 0;  
       private static final Object lock= new Object();    
       public synchronized void foo() 
      {  
          synchronized(Test.class)
         {  
             count++;  
         }  
      }  
    }

Надеюсь, вы найдете этот ответ полезным.

Ответ 5

Если это только одно целое, вы также можете использовать

Interlocked.Increment(count)

(пространство имен - System.Threading).