英文:
How do I avoid call to same method only if arguments are same, on different threads
问题
这个方法startProcess
中:
private void startProcess(Long id)
throws UnAuthorizedException, UnAuthenticatedException, InvalidRequestException {
......
createComponent(Long id);
......
}
如果id
相同,不应在单独的线程上再次调用createComponent
方法或者startProcess
本身。
英文:
private void startProcess(Long id)
throws UnAuthorizedException, UnAuthenticatedException, InvalidRequestException {
......
createComponent(Long id);
......
}
This method createComponent or the startProcess itself should not be called again on separate thread, if the id is same
答案1
得分: 0
如果您的方法需要一段时间,并且您只想在ID相同时(或任何参数的组合时)阻止并发访问,那么您需要为每个ID(或任何参数的组合)存储一个锁。
这些锁可以存储在一个 Map
中。
如果它只是一个类型为 Long
的ID,您可以直接将其用作Map的键。如果您有多个参数,请将它们与所需的equals/hashcode方法一起包装在一个自定义类中。
private Map<Long, Lock> locks = new ConcurrentHashMap<>(100);
private void startProcess(Long id) {
Lock lock = locks.computeIfAbsent(id, id2 -> new ReentrantLock());
lock.lock();
try {
System.out.println(System.currentTimeMillis() + " Starting " + id + " on thread " + Thread.currentThread().getName());
try { Thread.sleep(2000); } catch (InterruptedException ex) { }
System.out.println(System.currentTimeMillis() + " Done with " + id + " on thread " + Thread.currentThread().getName());
} finally {
lock.unlock();
locks.remove(lock);
}
}
调用方式如下:
new Thread(() -> startProcess(1L), "A").start();
new Thread(() -> startProcess(1L), "B").start();
new Thread(() -> startProcess(2L), "C").start();
这是可能的输出之一:
1595950202869 Starting 1 on thread A
1595950202879 Starting 2 on thread C
1595950204870 Done with 1 on thread A
1595950204870 Starting 1 on thread B
1595950204879 Done with 2 on thread C
1595950206870 Done with 1 on thread B
[编辑] 参数的自定义类示例代码:
private Map<ProcessParams, Lock> locks = new ConcurrentHashMap<>(100);
private void startProcess(long id, String name) {
Lock lock = locks.computeIfAbsent(new ProcessParams(id, name), id2 -> new ReentrantLock());
lock.lock();
try {
System.out.println(System.currentTimeMillis() + " Starting " + name + " on thread " + Thread.currentThread().getName());
try { Thread.sleep(2000); } catch (InterruptedException ex) { }
System.out.println(System.currentTimeMillis() + " Done with " + name + " on thread " + Thread.currentThread().getName());
} finally {
lock.unlock();
locks.remove(lock);
}
}
public static class ProcessParams {
private final long id;
private final String name;
public ProcessParams(long id, String name) {
this.id = id;
this.name = Objects.requireNonNull(name, "name");
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof ProcessParams))
return false;
ProcessParams other = (ProcessParams)o;
return id == other.id && name.equals(other.name);
}
@Override
public int hashCode() {
int hashCode = 0x811C9DC5;
hashCode = 0x01000193 * (hashCode ^ Long.hashCode(id));
hashCode = 0x01000193 * (hashCode ^ name.hashCode());
return hashCode;
}
}
英文:
If your method takes time and you want to block concurrent access only when the ID is the same (or any combination of parameters) then you need to store a lock for every ID (or any combination of parameters).
These locks can be stored in a Map
.
If it's just an ID of type Long
you can use it directly as the Map's key. If you have multiple parameters, wrap them in a custom class along with the required equals/hashcode methods.
private Map<Long, Lock> locks = new ConcurrentHashMap<>(100);
private void startProcess(Long id) {
Lock lock = locks.computeIfAbsent(id, id2 -> new ReentrantLock());
lock.lock();
try {
System.out.println(System.currentTimeMillis() + " Starting " + id + " on thread " + Thread.currentThread().getName());
try { Thread.sleep(2000); } catch (InterruptedException ex) { }
System.out.println(System.currentTimeMillis() + " Done with " + id + " on thread " + Thread.currentThread().getName());
} finally {
lock.unlock();
locks.remove(lock);
}
}
Called with:
new Thread(() -> startProcess(1L), "A").start();
new Thread(() -> startProcess(1L), "B").start();
new Thread(() -> startProcess(2L), "C").start();
this is one possible output:
1595950202869 Starting 1 on thread A
1595950202879 Starting 2 on thread C
1595950204870 Done with 1 on thread A
1595950204870 Starting 1 on thread B
1595950204879 Done with 2 on thread C
1595950206870 Done with 1 on thread B
[Edit] Sample code for custom class for parameters:
private Map<ProcessParams, Lock> locks = new ConcurrentHashMap<>(100);
private void startProcess(long id, String name) {
Lock lock = locks.computeIfAbsent(new ProcessParams(id, name), id2 -> new ReentrantLock());
lock.lock();
try {
System.out.println(System.currentTimeMillis() + " Starting " + name + " on thread " + Thread.currentThread().getName());
try { Thread.sleep(2000); } catch (InterruptedException ex) { }
System.out.println(System.currentTimeMillis() + " Done with " + name + " on thread " + Thread.currentThread().getName());
} finally {
lock.unlock();
locks.remove(lock);
}
}
public static class ProcessParams {
private final long id;
private final String name;
public ProcessParams(long id, String name) {
this.id = id;
this.name = Objects.requireNonNull(name, "name");
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof ProcessParams))
return false;
ProcessParams other = (ProcessParams)o;
return id == other.id && name.equals(other.name);
}
@Override
public int hashCode() {
int hashCode = 0x811C9DC5;
hashCode = 0x01000193 * (hashCode ^ Long.hashCode(id));
hashCode = 0x01000193 * (hashCode ^ name.hashCode());
return hashCode;
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论