如何避免线程饿死

时间:2015-12-08 22:16:32

标签: java multithreading

假设我有一间带m房的酒店。

嘉宾(主题)一直进出。

一个房间里面可以有很多人,但只有一个房间会有人。例如:

  • 嘉宾A(想要房间1)
  • 嘉宾B(想要2室)
  • 嘉宾C(想要房间1)

    一旦所有房间都空了,A可以去1号房间;

    B还不能去2号房间,因为还有另一个房间还有人在里面;

    C可以去1号房间,因为C室想要的是唯一有人在里面的房间;

  • 鉴于A和C离开1号房间,B应该可以去2号房间

  • 退出房间的最后一位客人应该阻止其他客人(避免他们在外出时进来),直到它离开,以便其他人可以继续

我怎么能以某种方式实现这个线程不会饿死?

为简单起见,假设一旦宾客进入一个房间,它会睡几秒钟然后离开。这是我的(错误的)实现:

import java.util.ArrayList;
import java.util.Random;

public class Guest extends Thread {
    static Rooms rooms = new Rooms(5);

    int id;

    Guest(int id) {
        this.id = id;
    }

    public void run() {

        rooms.join(this);

        nap();

        rooms.quit(this);
    }

    public void nap() {
        try {
            sleep((new Random().nextInt(4000) + 1000));
        } catch (InterruptedException e) {
        }
    }

    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 20; i++) {

            Thread t = new Guest(i);
            t.start();

            Thread.sleep((long) new Random().nextInt(1500) + 1000);

        }

    }

}

class Rooms {

    Room[] rooms;
    int busy;

    Rooms(int m) {
        busy = -1;
        rooms = new Room[m + 1];
        for (int i = 0; i < m + 1; i++)
            rooms[i] = new Room();
    }

    void join(Guest h) {
        if (busy == -1) {
            busy = (new Random().nextInt(rooms.length));
        }

        rooms[busy].add(h);

        System.out.println("Guest " + h.id + " came inside room " + busy + " with " + rooms[busy].size() + " people");
    }

    void quit(Guest h) {

        if (rooms[busy].size() == 1) {
            setHandler(busy, h);
        } else {
            rooms[busy].remove(h);
            System.out
                    .println("Guest " + h.id + " came out of room " + busy + " with " + rooms[busy].size() + " people");
        }

    }

    synchronized void setHandler(int numQuarto, Guest ultimo) {
        System.out.println("(Last) Guest " + ultimo.id + " came out of room " + busy + " with "
                + rooms[numQuarto].size() + " people");
        rooms[numQuarto].remove(ultimo);
        busy = -1;
    }

}

class Room extends ArrayList<Guest> {
}

2 个答案:

答案 0 :(得分:1)

使用线程执行此操作 - 这是非常人为的,但我认为这是一个练习 - 您需要学习如何在某个条件下进行Thread等待,以及如何通知一个或多个{ {1}}他们等待的条件可能已经满足。方便的是,每个对象都有用于这些目的的方法Threadswait()notify()

wait / notify的一个重要考虑因素是您必须小心等待并通知正确的对象。通常,您想要受影响的notifyAll(),而是所有相关线程所依赖的共享对象,以实现相互同步。在这种特殊情况下,看起来Thread会很好。

一般的想法是在Guest.rooms中,当前线程测试它是否可以立即占用一个房间。如果是这样,它确实如此,现在继续。但是,如果没有,则调用Rooms.join()(在wait()实例上,此时为Rooms)。每当this返回时,线程必须再次检查它是否可以立即占用它想要的房间;如果没有,它必须再次等待。

另一半将在wait()。每当运行该方法的线程是房间外的最后一个线程时,它必须重置Rooms.quit()以指示没有占用空间,然后,重要的是,调用busy让所有线程在那时等待知道有机会得到一个房间。

您会发现需要对此进行适当的同步。特别是,您只能在持有目标对象的监视器时调用notifyAll()wait()(以及notifyAll())(它将在等待期间释放,并在等待之前重新获取()返回)。您还需要确保正确同步共享对象的所有操作,但是,在这种情况下主要是notify()及其成员,而不会阻止线程继续执行。特别要注意的是,Rooms 的线程会释放他们可能持有的任何监视器。

剩下的由你决定。我给你的提示比我应该提供的更多,但是学习如何正确使用wait / notify确实有点棘手。

答案 1 :(得分:1)

你做不到。根据您的要求,需要实施其他机制,以确保不会发生饥饿。例如,

  

嘉宾(主题)一直进出。

因此, n 线程有可能在整个时间内进入Room m 。也有可能在那段时间里有更多的线程需要另一个房间。但是,在Room m 首次清空(可能永远不会发生)之前,他们无法进入房间。这可以继续任意数量的房间和线程。即使......就是这种情况。

  

为简单起见,假设一旦宾客进入房间,就会发生这种情况   睡了几秒然后就出去了。

那是因为......

  

C可以去1号房间,因为C室想要的是唯一的房间   里面的人;

这意味着另一个线程可能会进入一个已经占用的房间,其中有一个或多个线程,其中 t 时间要休眠。新线程进入休眠状态,直到上一个线程之后才会唤醒。在睡觉 n 时,更多线程可能进入房间,可能导致其他线程等待其他房间饿死。