如何在Java中运行类的不同实例的线程之间同步静态变量?


120

我知道synchronize在方法带来同步到该对象之前使用关键字。也就是说,运行对象的同一实例的2个线程将被同步。

但是,由于同步是在对象级别进行的,因此运行对象的不同实例的2个线程将不会同步。如果在Java类中有一个由该方法调用的静态变量,则希望在类的各个实例之间进行同步。这两个实例在2个不同的线程中运行。

我们可以通过以下方式实现同​​步吗?

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

确实如此,因为我们已经定义了一个lock静态对象,并且正在使用该synchronized锁的关键字,所以static变量count现在可以在class实例之间进行同步Test


4
除非将锁对象声明为FINAL,否则所有这些答案都是USELESS!

另请
参阅

Answers:


193

有几种同步对静态变量的访问的方法。

  1. 使用同步静态方法。这将在类对象上同步。

    public class Test {
        private static int count = 0;
    
        public static synchronized void incrementCount() {
            count++;
        }
    } 
  2. 在类对象上显式同步。

    public class Test {
        private static int count = 0;
    
        public void incrementCount() {
            synchronized (Test.class) {
                count++;
            }
        }
    } 
  3. 在其他静态对象上同步。

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

在很多情况下,方法3是最好的,因为锁对象没有暴露在类之外。


1
1.第一个甚至不需要锁对象,这不是最好的吗?
Baiyan Huang

4
2.将count声明为volatile也可以,因为volatile确保变量已同步。
Baiyan Huang

9
#3最好的原因是,任何随机的代码段都可以同步,Test.class并可能破坏您的一天。另外,类初始化在锁定类的情况下运行,因此,如果您对类的初始化感到疯狂,您可能会头疼。 volatile没有帮助,count++因为这是一个读/修改/写序列。如另一个答案中所述,java.util.concurrent.atomic.AtomicInteger这里可能是正确的选择。
fadden

4
如果要读取其他线程设置的正确值,请不要忘记按计数同步读取操作。声明它为易失性(除了同步写入外)也将对此有所帮助。
n0rm1e 2013年

1
@Ferrybig不,您正在锁定Test.classthis将是同步非静态方法的锁
user3237736 '18

64

如果您只是共享一个计数器,请考虑使用AtomicInteger或java.util.concurrent.atomic包中的另一个合适的类:

public class Test {

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

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

3
它在Java 1.5中可用,而在1.6中不可用。
Pawel

4

是的,它是真的。

如果您创建类的两个实例

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

然后,t1.foo和t2.foo都在同一个静态对象上同步,因此彼此阻塞。


一个人会阻止另一个人,而不是彼此照顾对方。
贾法·阿里2013年

0

您可以在类上同步代码。那将是最简单的。

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

希望您觉得这个答案有用。


2
这将起作用,但是正如@Fadden在其他地方提到的那样,请注意,任何其他线程也可以同步Test.class并影响行为。这就是为什么最好进行同步的原因lock
sbk 2013年

你说的是对的。这就是为什么我明确提到以上是最简单的方法的原因。
贾法里·阿里

0

我们还可以使用ReentrantLock实现静态变量的同步。

public class Test {

    private static int count = 0;
    private static final ReentrantLock reentrantLock = new ReentrantLock(); 
    public void foo() {  
        reentrantLock.lock();
        count = count + 1;
        reentrantLock.unlock();
    }  
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.