不同线程上的Java锁定和解锁

7
我有一个主线程和一个工作线程。主线程将任务添加到队列中,工作线程取出任务并计算数据。在将对象放入队列之前,我在任务对象上调用 ReentrantLock 对象的锁定(在主线程上)。当工作线程完成从队列中的任务时,我在其上调用解锁操作(在工作线程上)。问题在于,我会因为在不同的线程上调用锁定和解锁而收到 IllegalMonitorStateException 异常。
我正在寻找一种替代的锁系统,在其中可以在不同的线程上执行此操作。
示例:
public class Worker extends Thread {
    public static Queue<Task> tasks = new ConcurrentLinkedQueue<Task>();

    @Override
    public void run() {
        while (true) {
            Task task = tasks.poll();

            if (task != null) {
                task.work();
                task.lock.unlock(); // Here is the unlock, Task#i should not change up to now
            }
        }
    }
}


public class Task {
    private int i = 0;
    public Lock lock;

    public void setI(int i) {
        lock.lock();
        this.i = i;
        lock.unlock();
    }

    public void work() {
        System.out.println(i);
    }
}


public class Test {
    Task task = new Task();

    public void addTask() {
        task.lock.lock(); // Here is the lock, Task#i should not change
        Worker.tasks.add(task);
    }
}

你想用锁保护什么? - Savior
你能否发布你的代码以更好地理解问题? - Ravindra babu
我会发布一个例子。 - stonar96
3
你可以使用“Semaphore”而不是“ReentrantLock”来实现你想要的功能,但在一个线程中锁定,在另一个线程中解锁并不是保护数据的有效方式。 - Solomon Slow
@stonar96 - 像这样一个简单的检查,例如如果项目在队列中,则不要在主线程内编辑它,会起作用吗?如果它不在队列中,则主线程可以尝试获取锁定。如果无法获取它,则意味着工作线程正在处理它。因此,它应该等待或执行其他操作。 - MS Srikkanth
显示剩余8条评论
3个回答

9
为什么不使用只有一个许可的Semaphore呢?与锁操作不同,您需要获取单个许可。您应该始终使用release()释放锁定。

一个Semaphore可以在一个线程中获取,在另一个线程中释放,而锁必须在同一个线程中被锁定/解锁,这是真的吗? 我的应用程序是这样的:在线程1中,我使用无限循环运行工作 while(true){ ... theLock.lock(); //这是为了确保上一次运行的所有工作都已完成,然后执行几个工作,所有这些工作都是异步的,在不同的线程中,每个线程完成后都会向主线程发送一条消息 }, 在线程2中,接收到完成的工作消息后,解锁锁定。Semaphore可以工作,但ReentrantLock不能。 - Yulin

0
根据问题,设计多线程应用程序的方式似乎不正确。
要么工作线程应该处理对象的创建,要么您应该将不可变对象传递给工作线程,一旦工作线程完成,它可以将结果传递回主线程。
我认为在一个线程中获取锁定并在另一个线程中解锁是不可能的。

我无法重新设计应用程序,但可以修改应用程序。原始应用程序是单线程的,我想添加这个工作线程,它应该使用对象在被添加到队列时的状态来执行工作。 - stonar96

0

你不需要另外的锁系统。ConcurrentLinkedQueue 数据结构已经提供了自己的锁系统并保证了线程安全。任何额外的锁定都是不必要的。

但是你在重新发明轮子。我建议你看一下 ExecutorServiceThreadPools。虽然亲手构建东西是一个很好的学习经验,但也是错误的主要来源。

ExecutorService workerPool = Executors.newFixedThreadPool(10); // 10 worker threads
...
Runnable myTask = ...;
workerPool.submit(myTask); // called from the main thread
...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接