我有一个方法:
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);
由于
答案 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)
或其他一些线程可以从不同的对象访问它吗?
当然,他们can,synchronized
仅用于同步特定对象上的线程。我也不会将其设置为静态 - 实现您想要做的最简单的方法是使用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.
}
....
}
}