[英]Java multithreading - locking without waiting
我有一个Java多线程问题。 我有2个线程访问methodA(),该方法内部有一个for循环,并且在循环中调用methodB()。 应该使用线程名锁来锁定方法A,并且应该将方法B锁定在对其进行操作的对象ID上。 检查以下代码。
当前代码
private static final ConcurrentHashMap<Object, Object> LOCKS = new ConcurrentHashMap<Object, Object>();
private void methodA(){
LOCKS.putIfAbsent(Thread.currentThread().getName(), new Object()))
synchronized (LOCKS.putIfAbsent(Thread.currentThread().getName(), new Object())) {
for(loop through all objects) {
methodB(Object1);
}
}
}
private void methodB(Object1 object1) {
LOCKS.putIfAbsent(object1.getObjectId(), new Object()))
synchronized(LOCKS.putIfAbsent(object1.getObjectId(), new Object())){
//<Work on object1>
}
}
我已经完成了上面的代码,以确保2个不同的线程应该能够并行访问methodA(),但不能一次在methodB()中的同一Object1上工作(由methodA()调用)。 即 尽管我希望线程A和线程B同时访问methodA(),这又将循环遍历“ for”循环中的所有对象,并通过调用methodB()对每个对象进行操作,但我不希望线程A和B一次作用于SAME对象实例。 因此,以上代码基于对象实例ID锁定methodB()。
需要改进。
在上面的代码中,如果线程A和线程B来到methodB()并发现它们都想在同一个对象'obj1'上工作,则现在使用上面的代码,线程A将等待,线程B将等待另一个一个要完成的过程取决于谁先到达并锁定了methodB()。
但是,设想一种情况,其中线程A首先获得锁并执行methodB()需要9个小时来完成对'obj1'的处理。 在这种情况下,线程B将需要等待整整9个小时才能获得执行methodB()的机会,从而处理“ obj1”。
我不希望这种情况发生。 线程B一旦发现方法B()被线程A以'obj1'的名称锁定,则应继续(并稍后再返回obj1)尝试锁定和处理其他对象。 即 它应该尝试处理“ for”循环中的其他对象,例如对象列表中的obj1,obj2等。
解决此“无等待锁定”问题的任何输入将不胜感激。
非常感谢您的任何帮助。
一些澄清,以改善答案。
您可以做的最好的事情就是保持简单。
方法A应该使用线程名锁定来锁定
只有锁定共享对象才有意义。 锁定线程本地锁定是没有意义的。
已同步(LOCKS.putIfAbsent(object1.getObjectId(),new Object()))
这将返回null
并在首次运行时抛出NullPointerException。
我将代码替换为
private void methodA(){
List<Object1> objects = new ArrayList<>(this.objectList);
while(true) {
for(Iterator<Object1> iter = objects.iterator() : objects)
if(object1.methodB())
iter.remove();
if(objects.isEmpty()) break;
Thread.sleep(WAIT_TIME_BEFORE_TRYING_AGAIN);
}
}
// in class for Object1
final Lock lock = new ReentrantLock();
public boolean methodB() {
if (!lock.tryLock())
return false;
try {
// work on this
return true;
} finally {
lock.unlock();
}
}
根据您要如何处理无法锁定的对象,可以将它们添加到后台ExecutorService。 您可以让methodA重复调用所有其他失败的对象。
理想情况下,您将找到一种方法来最大程度地减少锁定时间,甚至完全不需要锁定。 例如,诸如AtomicReference和CopyOnWriteArrayList之类的类是线程安全且无锁的。
我不是Java专家,但我认为您不会通过同步实现这一目标。 我相信您将需要自己进行锁定。
如果创建java.util.concurrent.locks.ReentrantLock,则可以使用tryLock输入尚未锁定的锁。 methodA需要知道哪个methodB调用成功或哪个调用被取消,因为不可能进行锁定。 因此,您可以在methodA中进行锁处理,从而在此完全控制。 或者,您可以在methodB中进行锁定,但是如果methodB进行了工作或没有得到锁,那么您需要一些返回值或异常处理以向方法A发信号。
当然,您还需要在methodA中保留已通过的对象或仍需要处理的对象的列表。
一个线程传递偶尔会在传递中“丢失”一个对象是否重要? 如果不:
将所有对象存储在可锁定的队列式容器中。 让线程A,B等弹出一个,在其上调用方法,然后将其推回。然后,线程不可能同时在同一个对象上进行操作。 然后,唯一的锁定位于容器push / pop上,并且任何线程都无需在任何延长的时间进行阻塞。
..或类似的东西。 我总是尽量避免使用复杂的锁定方案-它们似乎总是搞砸了:(
我建议使用另一种方法。 与其直接调用该方法,不如将命令对象放入队列中,并让线程(或执行程序)处理命令。
当命令出现在队列中时,尝试获取锁。 如果这不起作用,请将命令添加到队列的末尾。 这将确保最终再次尝试命令。
缺点:如果每次尝试执行某个线程时,某个线程恰巧锁定了该命令,则该命令可能会无限期推迟。
解决方案是确保仅锁定所需的内容。 这样,当您看到“哦,这已被锁定”时,您就知道有人已经在执行该任务,您可以简单地忘记该命令(->不要重复执行两次)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.