【问题标题】:Synchronized method access from other objects JAVA从其他对象 JAVA 同步方法访问
【发布时间】:2016-08-12 13:41:51
【问题描述】:

我有一个方法:

private synchronized Long generateID (Short company) throws Exception {
    IDDAO iDDAO = SpringApplicationContext.getBean (IDDAO.class);
    ID iD = iDDAO.findByNaturalKey (new IDNatKey (company);

    if (iD != null) {
        // Check if ID has reached limit, then reset the ID to the first ID
        if (iD.getLatestIDno ().longValue () == iD.getLastIDno ().longValue ()) {
            iD.setLatestIDno (iD.getFrstIDno ());
        }

        // Get next ID
        iD.setLatestIDno (iD.getLatestIDno () + 1);
        // update database with latest id
        iDDAO.update (iD);

        return iD.getLatestIDno ();
    }
}

在这段代码中,我正在更新 ID iD.setLatestIDno(iD.getLatestIDno() + 1) 的值。这是以同步方式完成的,因此从多个线程访问时它永远不会重复。

我的问题是如果使这个方法同步会阻止其他线程访问它?还是其他线程可以从不同的对象访问它?那么它应该是静态的吗?

代码是这样使用的

Long check = generateID (123);

谢谢

【问题讨论】:

  • 有人可以通过编辑我的代码来告诉我如何使用 AtomicLong 中的 incrementAndGet() 吗?

标签: java multithreading concurrency synchronized


【解决方案1】:

其他线程可以从其他实例访问它:

public synchronized Long generateID(Short company) {
    // do something
}

相当于:

public Long generateID(Short company) {
    synchronized(this) {
        // do something
    }
}

所以如果this 引用不同的实例,线程不会阻塞。

如果你想在不同的实例上同步线程,你需要提供一个公共锁:

Object lock = new Object();
MyClass c1 = new MyClass(lock);
MyClass c2 = new MyClass(lock);
// ...

// in MyClass:
private Long generateID(Short company) {
    synchronized(lock) {
        // do something
    }
}

您也可以在对象上使用Lock 而不是synchronized。但逻辑仍然相似。

由于您使用的是SpringApplicationContext,因此您可以创建一个ReentrantLock 并将其添加到您的上下文中,然后像访问IDDAO 实例一样访问它。

【讨论】:

  • 它是一个私有方法,所以看起来它只能被this访问,就像原来写的那样,永远不会被其他对象访问。
  • 有没有办法为每个人、所有实例锁定整个方法而不使用公共锁?
  • 我不得不怀疑 OP 是否在变相询问XY Problem,并且不得不想知道这个问题背后的动机是什么。
  • @Aiden:请备份——您要解决什么总体问题?请提供相关背景资料。
  • @HovercraftFullOfEels 无论如何它都是从一些公共方法调用的。喜欢:public updateComnpany(short companyId) { Lond id = generateID(companyId); ... }
【解决方案2】:

或者其他一些线程可以从不同的对象访问它?

当然,它们cansynchronized 仅用于在特定对象上同步线程。我也不会将其设为静态 - 实现您想要做的最简单的方法是使用 AtomicInteger (javadoc) 类中的 incrementAndGet() 方法。

定义:

public static AtomicInteger counter = new AtomicInteger(0); 
// starting number is 0, but can be changed

用法:

int id = counter.incrementAndGet(); // assigns number 1
int otherId = counter.incrementAndGet(); // assigns 2

你可以在谷歌上找到很多例子,f.e. this one。 AtomicInteger 提供了许多其他方法,我建议您查看 javadoc(参见第二个链接)。

说实话,我不明白你为什么要重置 ID - 但好吧,假设你需要这样做。当 ID 超过限制时(假设为常量MAXIMUM_ID),有很多选项可以做到这一点:

  • 使用模数:counter.incrementAndGet() % MAXIMUM_ID,但可能会发生溢出,因此您需要注意这一点;

  • 简单的解决方案可能是重置计数器:

    id = counter.incrementAndGet(); // too high
    if(id > MAXIMUM_ID) {
       counter = new AtomicInteger(0);
       id = counter.incrementAndGet(); // gets 1
    }   
    

防止溢出。但随后可能会发生并发问题 - 假设创建了两个新计数器,因此它们都获得编号 1。因此,无需创建新的 AtomicInteger,您只需通过方法 set(int value) 重置值(有关更多信息,请参阅 javadoc):

counter.set(0);

通常使用这种方法,因为它应该更快,并且您确定它是线程安全的(还有,为什么要做已经完成的事情)。

【讨论】:

  • 哦,我没有注意到 - 你使用的是 long,所以使用 AtomicLong 而不是 AtomicInteger。很抱歉。
  • 如果你满意,你可以upwote。 :) 我建议你在编写 java 代码时使用camelCase。另外,如果您喜欢注释,请使用@Threadsafe
  • 我刚刚做了 :) 谢谢
【解决方案3】:

当 2 个线程即将在一个类中执行同步方法时,它们只会在它们处理对象的同一实例时发生冲突。在这种情况下,一次只有一个线程能够执行该方法。另一个线程需要等到第一个线程完成其方法调用。

请注意,即使线程进入睡眠状态,它也会持有其锁,因此您应该尽可能少地同步代码行。

这可以通过同步代码块(甚至是单行代码)而不是整个方法来实现。

class MyClass{
    public void myMethod(){
        ...
        synchronized(this){
            // All code in this block is synchronized.
        }
        ....
    }
}

如果您必须在不同的实例上同步操作,那么您应该将方法设为静态。完成此操作后,您可以同步整个方法:

class MyClass{
    public static synchronized void myMethod(){
        ...            
    }
}

或方法内的一段代码:

class MyClass{
    public static void myMethod(){
        ...
        Class c1 = Class.forName("MyClass");
        synchronized(c1){
            // All code in this block is synchronized.
        }
        ....
    }
}

【讨论】:

  • 我可以同步整个方法吗?方法是从主服务类调用的,该服务类由框架类调用。如果创建了多个类实例,我会很困惑,但我知道一件事,每个服务调用都是一个新事务,所以这意味着将创建多个对象。同步方法还能用吗?
  • 您可以简单地使用MyClass.class 而不是Class.forName("MyClass") 来引用您的课程。然后,您不需要处理潜在的ClassNotFoundException,因为已知该类在范围内。
猜你喜欢
  • 1970-01-01
  • 2018-09-09
  • 1970-01-01
  • 2016-11-24
  • 2014-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多