java - 两个线程死锁但不明白为什么,用 notifyAll() 释放了锁

标签 java concurrency monitor

使用 JConsole 时,当 2 个线程试图修改此对象时,我似乎遇到了死锁情况。

package com.steven.concurrent.assignment2.memoryallocator;
/*
 * This seems to deadlock... cant see why though.
 */
public class MemAllocMonitor implements IMemoryAllocator {

private final int MAX_FREE = 50;
private int freePages = MAX_FREE;

//I think this would work, without even the need for sync blocks.....
// But only in the situaion where i would not have to check the bounds of the updates. If it was just modification, this would be
// fine....
//private volatile int freePages = 50;

public MemAllocMonitor(int pages){
    assert(pages < MAX_FREE);
    this.freePages = pages;
}

public MemAllocMonitor(){

}

@Override
public synchronized void request(int number) {
    if(number < 0)
        throw new IllegalArgumentException();

    while(freePages - number < 0) {
        System.out.println("No space....waiting...");
        try {
            this.wait();                
        } catch (Exception e) {}
    }

        freePages -= number;
        System.out.println("Requested : " + number + " remaining " + freePages);

    this.notifyAll();

}

@Override
public synchronized void release(int number) {
    if(number < 0)
        throw new IllegalArgumentException();

    while(freePages + number > MAX_FREE) {
        System.out.println("page table full....would be " + (number + freePages) );
        try {
            this.wait();                
        } catch (Exception e) {}
    }

    freePages += number;
    System.out.println("Released : " + number + " remaining " + freePages);



    this.notifyAll();

}

@Override
public int getFreePages() {
    return freePages;
}

}

该对象通过一个实现可运行的简单包装器访问,并调用任一方法,如下所示。

package com.steven.concurrent.assignment2.memoryallocator;

import concurrent.RandomGenerator;
import concurrent.Time;

public class MemAllocRequester implements Runnable, MemoryAllocatorAction{

private IMemoryAllocator memoryAllocator;
private volatile boolean shutdown = false;;

public MemAllocRequester(IMemoryAllocator memAlloc){
    this.memoryAllocator = memAlloc;

}


@Override
public void run() {
    while(!shutdown){
        Time.delay(500);
        memoryAllocator.request(RandomGenerator.integer(0, 30));
    }

}

public void ShutDown(){
    this.shutdown = true;
}

}

package com.steven.concurrent.assignment2.memoryallocator;

import concurrent.RandomGenerator;
import concurrent.Time;

public class MemAllocReleaser implements Runnable, MemoryAllocatorAction{

private IMemoryAllocator memoryAllocator;
private volatile boolean shutdown = false;;

public MemAllocReleaser(IMemoryAllocator memAlloc){
    this.memoryAllocator = memAlloc;

}


@Override
public void run() {
    while(!shutdown){
        Time.delay(500);
        memoryAllocator.release(RandomGenerator.integer(0, 30));
    }

}

public void ShutDown(){
    this.shutdown  = true;
}

它是这样开始的......

package com.steven.concurrent.assignment2.memoryallocator;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MemAllocMain {


public static void main(String[] args){

    ExecutorService executor = Executors.newFixedThreadPool(10);


    //IMemoryAllocator memoryAllocator = new MemAllocSemaphore();
    IMemoryAllocator memoryAllocator = new MemAllocMonitor();


    System.out.println("Starting app with " + memoryAllocator.getFreePages() + " pages...");

    Thread t1 = new Thread(new MemAllocRequester(memoryAllocator));
    Thread t2 = new Thread(new MemAllocReleaser(memoryAllocator));

    t1.setName("MEMORY REQUESTER £££££££££££££££££££");
    t2.setName("MEMORY RELEASER £££££££££££££££££££");

    executor.submit(t1);
    executor.submit(t2);


}

}

我已经使用信号量类实现了一个解决方案,但出于某种原因,这会导致使用默认的 java 监视器解决方案出现问题。它运行大约 30 秒,然后两个线程都进入等待状态,即使应该强制执行锁定也是如此。

最佳答案

问题是两个线程同时达到上限和下限(分别为 50 和 0)。下面的两个示例都强调了僵局。

场景一

  1. 请求(29) - freePages=21
  2. request(30) - 低于 0,所以等待
  3. release(30) - 超过 50 所以等待:死锁

场景 2

  1. 请求(29) - freePages=21
  2. release(30) - 超过 50 等一下
  3. request(30) - 低于 0 所以等待:死锁

我不确定作业问题的具体要求是什么,但您需要重新访问发布和请求方法。我看到两个可行的解决方案:

  1. 更改释放方法,使其最多释放 MAX_FREE 但仍会返回
  2. 更改释放方法,以便它可以释放所请求数量的子集,notifyAll,重新进入等待状态,以便它可以释放剩余数量。

此外,您在使用 ExecutionService 时有点不对。 ExecutionService 是创建线程的原因,因此您没有理由像现在这样创建线程。

Thread t1 = new Thread(new MemAllocRequester(memoryAllocator));
Thread t2 = new Thread(new MemAllocReleaser(memoryAllocator));

您正在创建的线程实际上永远不会作为线程“启动”。它仍然为您工作,因为 ExecutionService 线程将调用您的 Thread.run(),而 Thread.run() 将调用 MemAlloc*.run()。即,您的 t1 和 t2 线程只是传递 run() 调用,不提供任何值。

您的 MemAllocRequester 和 MemAllocReleaser 是 Runnable,因此只需将它们直接传递到 ExecutionService。

executor.submit(new MemAllocRequester(memoryAllocator));
executor.submit(new MemAllocReleaser(memoryAllocator));

关于java - 两个线程死锁但不明白为什么,用 notifyAll() 释放了锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13301385/

相关文章:

java - MobileFirst 到 Servlet 的适配器

python - 可以在不暂停 Python 解释器的情况下在后台运行异步事件循环吗?

java - 并发和多线程有什么区别?

c# - 当线程运行相同的方法时,为什么值不冲突?

html - 如何防止显示器在观看 html 5 动画时休眠

java - Eclipse 在服务器上调试/运行

java - 如何在不编写已经很简单的方法的情况下简化类?

c# - 多线程集差的有效方法

java - Apache HttpClient 不使用分页链接获取页面内容。我得到 200 状态,但 html 没有内容

node.js - NodeJS 和 Forever(监控和重启应用程序)