Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/319.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
对象上的Java同步方法锁,还是方法?_Java_Multithreading_Thread Safety_Locking_Synchronized - Fatal编程技术网

对象上的Java同步方法锁,还是方法?

对象上的Java同步方法锁,还是方法?,java,multithreading,thread-safety,locking,synchronized,Java,Multithreading,Thread Safety,Locking,Synchronized,如果在同一个类中有两个同步方法,但每个方法访问不同的变量,那么两个线程可以同时访问这两个方法吗?锁是发生在对象上,还是与同步方法中的变量一样具体 例如: class X { private int a; private int b; public synchronized void addA(){ a++; } public synchronized void addB(){ b++; } } 两个线程能否

如果在同一个类中有两个同步方法,但每个方法访问不同的变量,那么两个线程可以同时访问这两个方法吗?锁是发生在对象上,还是与同步方法中的变量一样具体

例如:

class X {

    private int a;
    private int b;

    public synchronized void addA(){
        a++;
    }

    public synchronized void addB(){
        b++;
    }

}

两个线程能否同时访问类X的同一个实例执行
X.addA(
)和
X.addB()

访问的锁在对象上,而不是在方法上。在方法中访问哪些变量是无关的

向方法中添加“synchronized”意味着运行代码的线程必须在继续之前获取对象的锁。添加“static synchronized”意味着运行代码的线程必须在继续之前获得类对象上的锁。或者,您可以将代码包装在块中,如下所示:

public void addA() {
    synchronized(this) {
        a++;
    }
}
以便您可以指定必须获取其锁的对象

如果要避免锁定包含的对象,可以选择:

  • 使a和b原子化(使用java.util.concurrent.atomic)

在方法声明上同步的语法是:

 public void addA() {
     synchronized (this) {
          a++;
     }
  }
 ClassA {
     public static void addA() {
          synchronized(ClassA.class) {
              a++;
          }
 }
在静态方法中,这是语法上的糖分:

 public void addA() {
     synchronized (this) {
          a++;
     }
  }
 ClassA {
     public static void addA() {
          synchronized(ClassA.class) {
              a++;
          }
 }

我认为,如果Java设计人员当时知道现在对同步的理解,他们就不会添加语法糖,因为它通常会导致并发性的糟糕实现。

如果您将该方法声明为已同步(就像您通过键入
public synchronized void addA()
所做的那样)您可以对整个对象进行同步,因此两个线程访问来自同一对象的不同变量时会相互阻塞

如果您希望一次只同步一个变量,这样两个线程在访问不同变量时不会互相阻塞,那么您可以在
synchronized()
块中分别对它们进行同步。如果
a
b
是对象引用,您将使用:

public void addA() {
    synchronized( a ) {
        a++;
    }
}

public void addB() {
    synchronized( b ) {
        b++;
    }
}
但既然它们是原始的,你就不能这么做

我建议您改用AtomicInteger:


您可以执行以下操作。在这种情况下,您使用a和b上的锁来同步,而不是“this”上的锁。我们不能使用int,因为原语值没有锁,所以我们使用Integer

class x{
   private Integer a;
   private Integer b;
   public void addA(){
      synchronized(a) {
         a++;
      }
   }
   public synchronized void addB(){
      synchronized(b) {
         b++;
      }
   }
}

这可能不起作用,因为从Integer到int的装箱和自动装箱以及viceversa依赖于JVM,如果两个不同的数字在-128到127之间,很可能会被散列到同一地址

如果您有一些不同步的方法,并且正在访问和更改实例变量。在您的示例中:

 private int a;
 private int b;
当其他线程处于同一对象的同步方法中时,任意数量的线程都可以同时访问这些非同步方法,并且可以更改实例变量。 例如:-

 public void changeState() {
      a++;
      b++;
    }
您需要避免非同步方法访问实例变量并对其进行更改的情况,否则就没有必要使用同步方法

在以下场景中:-

class X {

        private int a;
        private int b;

        public synchronized void addA(){
            a++;
        }

        public synchronized void addB(){
            b++;
        }
     public void changeState() {
          a++;
          b++;
        }
    }

只有一个线程可以在addA或addB方法中,但同时任何数量的线程都可以进入changeState方法。没有两个线程可以同时输入addA和addB(因为对象级锁定),但是任何数量的线程都可以同时进入changeState。

来自oracle文档

使方法同步有两种效果:

首先,同一对象上的两个同步方法调用不可能交错。当一个线程为一个对象执行同步方法时,调用同一对象块的同步方法的所有其他线程(暂停执行),直到第一个线程对该对象执行完毕

其次,当同步方法退出时,它会自动与同一对象的同步方法的任何后续调用建立“发生在之前”关系。这保证了对对象状态的更改对所有线程都是可见的

请阅读此文档以了解内在锁和锁行为

这将回答您的问题:在同一个对象x上,当其中一个同步方法正在执行时,您不能同时调用x.addA()和x.addB()。

这个示例(虽然不是很好)可以更深入地了解锁定机制。如果incrementA已同步,而incrementB未同步,则incrementB将尽快执行,但如果incrementB也已同步,则它必须“等待”incrementA完成,然后incrementB才能执行其工作

这两种方法都被调用到单个实例对象上,在本例中是:job,而“竞争”线程是aThread和main

尝试在incrementB中使用“synchronized”和不使用它,您将看到不同的结果。如果incrementB也是“synchronized”,则它必须等待incrementA()完成。每种变体运行几次

class LockTest implements Runnable {
    int a = 0;
    int b = 0;

    public synchronized void incrementA() {
        for (int i = 0; i < 100; i++) {
            this.a++;
            System.out.println("Thread: " + Thread.currentThread().getName() + "; a: " + this.a);
        }
    }

    // Try with 'synchronized' and without it and you will see different results
    // if incrementB is 'synchronized' as well then it has to wait for incrementA() to finish

    // public void incrementB() {
    public synchronized void incrementB() {
        this.b++;
        System.out.println("*************** incrementB ********************");
        System.out.println("Thread: " + Thread.currentThread().getName() + "; b: " + this.b);
        System.out.println("*************** incrementB ********************");
    }

    @Override
    public void run() {
        incrementA();
        System.out.println("************ incrementA completed *************");
    }
}

class LockTestMain {
    public static void main(String[] args) throws InterruptedException {
        LockTest job = new LockTest();
        Thread aThread = new Thread(job);
        aThread.setName("aThread");
        aThread.start();
        Thread.sleep(1);
        System.out.println("*************** 'main' calling metod: incrementB **********************");
        job.incrementB();
    }
}
class LockTest实现可运行{
int a=0;
int b=0;
公共同步的无效增量A(){
对于(int i=0;i<100;i++){
这个.a++;
System.out.println(“Thread:+Thread.currentThread().getName()+”a:+this.a);
}
}
//尝试使用“synchronized”和不使用它,您将看到不同的结果
//如果incrementB也是“同步”的,那么它必须等待incrementA()完成
//公共无效增量b(){
公共同步的无效增量B(){
这是.b++;
System.out.println(“*******************增量B****************************”);
System.out.println(“Thread:+Thread.currentThread().getName()+”b:+this.b);
System.out.println(“*******************增量B********************************”
class X {

private int a;
private int b;

public void addA(){
    a++;
}

public void addB(){
    b++;
}}