Java同步方法locking对象或方法?

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

例:

class X { private int a; private int b; public synchronized void addA(){ a++; } public synchronized void addB(){ b++; } } 

2个线程可以同时访问X类的同一个实例x.addA( )和x.addB()吗?

如果你声明方法是同步化的(就像通过inputpublic synchronized void addA() )一样,你同步整个对象,所以两个线程访问同一个对象的不同variables将会阻塞对方。

如果你想一次同步一个variables,所以两个线程在访问不同的variables时不会互相阻塞,你可以在synchronized ()块中单独synchronized ()它们。 如果ab是对象引用,则可以使用:

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

但是因为它们是原始的,所以你不能这样做。

我build议你使用AtomicInteger来代替:

 import java.util.concurrent.atomic.AtomicInteger; class X { AtomicInteger a; AtomicInteger b; public void addA(){ a.incrementAndGet(); } public void addB(){ b.incrementAndGet(); } } 

同步的方法声明是这样的语法糖:

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

在静态方法上,它是这样的语法糖:

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

我认为,如果Javadevise者知道现在对于同步的理解,他们不会添加语法糖,因为它往往导致并发性的不好实现。

访问的锁在对象上,而不在方法上。 在方法中访问哪些variables是不相关的。

为方法添加“synchronized”意味着运行代码的线程在继续之前必须获取对象上的锁。 添加“静态同步”意味着运行代码的线程在继续之前必须获取类对象上的锁。 另外,你也可以把代码包装在这样的块中:

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

以便您可以指定必须获取locking的对象。

如果你想避免locking包含对象,你可以select:

  • 使用指定不同锁的同步块
  • 使a和bprimefaces(使用java.util.concurrent.atomic)

从oracle文档链接

使方法同步有两个作用:

首先,同一个对象上的两个同步方法的调用是不可能交错的。 当一个线程正在执行一个对象的同步方法时,所有其他调用同一对象的同步方法的线程将阻塞(挂起执行),直到第一个线程完成对象。

其次,当一个同步的方法退出时,它会自动build立一个与先前同步对象的任何后续调用同步方法的before-before关系。 这保证了对所有线程都可见的对象状态的改变

看看这个文档页面了解内部锁和locking行为。

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

从Java SE关于同步方法的要领 :

首先, 同一个对象上的两个同步方法的调用是不可能交错的。 当一个线程正在执行一个对象的同步方法时,所有其他调用同一对象的同步方法的线程将阻塞(挂起执行),直到第一个线程完成对象。

从Java SE的同步块要点 :

同步语句对于通过细粒度同步来提高并发性也很有用。 假设,例如,类MsLunch有两个实例字段c1和c2,它们从不一起使用。 这些字段的所有更新都必须同步, 但没有理由阻止更新c1与更新c2进行交错,并且通过创build不必要的阻塞来降低并发性。 而不是使用同步的方法或使用与此相关的锁,我们创build两个对象来提供锁。

(强调我的)

你有2个variables没有交错。 所以你想同时从不同的线程访问每一个线程。 你需要定义锁而不是在对象类本身上,而是在下面的类Object(例如来自第二个Oracle链接的例子)上:

 public class MsLunch { private long c1 = 0; private long c2 = 0; private Object lock1 = new Object(); private Object lock2 = new Object(); public void inc1() { synchronized(lock1) { c1++; } } public void inc2() { synchronized(lock2) { c2++; } } } 

你可以做如下的事情。 在这种情况下,您正在使用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++; } } } 

如果你有一些不同步的方法正在访问和改变实例variables。 在你的例子中:

  private int a; private int b; 

当其他线程处于同一对象的同步方法中时,任何数量的线程可以同时访问这些非同步方法,并且可以更改实例variables。 例如: –

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

您需要避免非同步方法正在访问实例variables并更改它的情况,否则没有必要使用同步方法。

在下面的情况下: –

 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方法。 没有两个线程可以同时inputaddA和addB(由于对象级locking),但是同时有多个线程可以inputchangeState。

这个例子(虽然不是很漂亮)可以提供更多的locking机制的见解。 如果incrementA 同步 ,并且incrementB 不同步 ,那么incrementB将尽快执行,但是如果incrementB 同步,那么在incrementB能够完成它的工作之前,它必须等待incrementA完成。

这两个方法被调用到单个实例 – 对象,在这个例子中它是: 工作 ,和“竞争”线程是一个线程和主要

尝试与incrementB中的同步 ”,没有它,你会看到不同的结果。如果incrementB是“ 同步 ”,那么它必须等待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(); } } 

是的,它会阻塞另一个方法,因为同步方法适用于WHOLE类对象指向….但无论如何,它将阻止另一个线程执行,只要执行总和在任何方法addA或addB它进入,因为当它完成…一个线程将释放该对象,另一个线程将访问另一个方法等完美的工作。

我的意思是“synchronized”是为了阻止另一个线程访问另一个线程,而在特定的代码执行中。 所以最后这个代码将工作得很好。

最后要注意的是,如果有一个“a”和“b”variables,不仅仅是一个唯一的variables“a”或其他名称,也不需要同步这个方法,因为它是完全安全的,位置)。

 class X { private int a; private int b; public void addA(){ a++; } public void addB(){ b++; }} 

将工作以及

这可能无法正常工作,从Integer到int和viceversa的装箱和自动装箱都依赖于JVM,如果两个不同的数字在-128到127之间,可能会被哈希到相同的地址。