Java多线程 - 无需等待即可锁定

0 java multithreading locking wait

我有一个java多线程问题.我有两个线程访问一个方法A(),它在内部有一个for循环,在循环中调用一个方法B().应使用线程名锁定锁定方法A,并且应将方法B锁定在方法B操作的对象ID上.检查以下代码.

Currrent代码

        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>
      }   
    }
Run Code Online (Sandbox Code Playgroud)

我已经完成了上面的代码,以确保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等.

任何解决这个"无需等待锁定"问题的输入将不胜感激.

非常感谢您的帮助.

一些澄清,以改善答案.

  1. methodA()和methodB()都在同一个类中.methodB()不在Object类中.
  2. 实际上,线程A和线程B是调用包括A和B在内的许多方法的计时器线程.因此线程级别锁定(因为线程每15分钟左右被调用一次,并且有可能在第一次执行methodA()之前不会完成第二次打电话给它.
  3. methodB(Obj1)总是占用Object1参数,必须锁定它.原因是,在这个类中还有其他方法,例如methodC(Obj1)和methodD(Obj1),它们也接受Object1参数.对于Object1的同一实例,不应同时执行这些方法.因此需要锁定Object1参数.
  4. 线程B发现方法B(Obj1 obj)已经被obj1上的线程A()锁定,需要以某种方式再次调用methodB()但是使用不同的对象,比如说obj2.一旦它与其他人完成,它应该回到obj1.

Pet*_*rey 5

你能做的最好的事情就是保持简单.

应使用线程名锁定锁定方法A.

只有锁定共享对象才有意义.锁定线程本地锁是没有意义的.

synchronized(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();
    }
}
Run Code Online (Sandbox Code Playgroud)

根据您想要处理无法锁定的对象的方式,您可以将它们添加到后台ExecutorService.您可以让methodA重复调用此失败的所有剩余对象.

理想情况下,您会找到一种方法来最小化锁定的时间,甚至完全不需要锁定.例如,像AtomicReference和CopyOnWriteArrayList这样的类是线程安全且无锁的.