从其他对象JAVA访问同步方法

时间:2016-08-12 13:41:52

标签: java multithreading concurrency synchronized

我有一个方法:

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);

由于

3 个答案:

答案 0 :(得分:3)

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

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实例的方式访问它。

答案 1 :(得分:1)

  

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

当然,他们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

你可以在google上找到很多例子,例如: this one。 AtomicInteger提供了许多其他方法,我建议你看看javadoc(见第二个链接)。

Thruth被告知,我没有理解你为什么要重置ID - 但好吧,让我们说你需要这样做。当ID达到限制时(让我们说常量MAXIMUM_ID),有很多选项可以做到这一点:

  • 使用modulo:counter.incrementAndGet() % MAXIMUM_ID,但可能会发生溢出,因此您需要处理;

  • 天真的解决办法就是重置计数器:

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

这可以防止溢出。但随后可能发生并发问题 - 想象两个新计数器都会被创建,因此它们都会得到数字1.因此,您只需按方法set(int value)重置值,而不是创建新的AtomicInteger(有关详细信息,请参阅javadoc):< / p>

counter.set(0);

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

答案 2 :(得分:0)

当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.
        }
        ....
    }
}